mirror of
https://github.com/zadam/trilium.git
synced 2026-08-26 19:26:17 +02:00
Merge branch 'main' into renovate/electron-41.x
This commit is contained in:
@@ -544,14 +544,11 @@ li.dropdown-item a.dropdown-item-button:focus-visible {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#toast-container .toast .toast-header .btn-close {
|
||||
#toast-container .toast .toast-header .btn-close,
|
||||
#toast-container .toast .toast-close .btn-close {
|
||||
margin: 0 0 0 12px;
|
||||
}
|
||||
|
||||
#toast-container .toast.no-title {
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
#toast-container .toast .toast-body {
|
||||
flex-grow: 1;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
.modal .modal-header .btn-close,
|
||||
.modal .modal-header .help-button,
|
||||
.modal .modal-header .custom-title-bar-button,
|
||||
#toast-container .toast .toast-header .btn-close {
|
||||
#toast-container .toast .toast-header .btn-close,
|
||||
#toast-container .toast .toast-close .btn-close {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -46,12 +47,14 @@
|
||||
}
|
||||
|
||||
.modal .modal-header .btn-close,
|
||||
#toast-container .toast .toast-header .btn-close {
|
||||
#toast-container .toast .toast-header .btn-close,
|
||||
#toast-container .toast .toast-close .btn-close {
|
||||
--modal-control-button-hover-background: var(--modal-close-button-hover-background);
|
||||
}
|
||||
|
||||
.modal .modal-header .btn-close::after,
|
||||
#toast-container .toast .toast-header .btn-close::after {
|
||||
#toast-container .toast .toast-header .btn-close::after,
|
||||
#toast-container .toast .toast-close .btn-close::after {
|
||||
content: "\ec8d";
|
||||
font-family: boxicons;
|
||||
}
|
||||
@@ -67,7 +70,8 @@
|
||||
.modal .modal-header .btn-close:hover,
|
||||
.modal .modal-header .help-button:hover,
|
||||
.modal .modal-header .custom-title-bar-button:hover,
|
||||
#toast-container .toast .toast-header .btn-close:hover {
|
||||
#toast-container .toast .toast-header .btn-close:hover,
|
||||
#toast-container .toast .toast-close .btn-close:hover {
|
||||
background: var(--modal-control-button-hover-background);
|
||||
color: var(--modal-control-button-hover-color);
|
||||
}
|
||||
@@ -75,19 +79,22 @@
|
||||
.modal .modal-header .btn-close:active,
|
||||
.modal .modal-header .help-button:active,
|
||||
.modal .modal-header .custom-title-bar-button:active,
|
||||
#toast-container .toast .toast-header .btn-close:active {
|
||||
#toast-container .toast .toast-header .btn-close:active,
|
||||
#toast-container .toast .toast-close .btn-close:active {
|
||||
transform: scale(.85);
|
||||
}
|
||||
|
||||
.modal .modal-header .btn-close:focus,
|
||||
.modal .modal-header .help-button:focus,
|
||||
#toast-container .toast .toast-header .btn-close:focus {
|
||||
#toast-container .toast .toast-header .btn-close:focus,
|
||||
#toast-container .toast .toast-close .btn-close:focus {
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.modal .modal-header .btn-close:focus-visible,
|
||||
.modal .modal-header .help-button:focus-visible,
|
||||
#toast-container .toast .toast-header .btn-close:focus-visible {
|
||||
#toast-container .toast .toast-header .btn-close:focus-visible,
|
||||
#toast-container .toast .toast-close .btn-close:focus-visible {
|
||||
outline: 2px solid var(--input-focus-outline-color);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@@ -2093,7 +2093,10 @@
|
||||
"process_now": "Process OCR",
|
||||
"processing": "Processing...",
|
||||
"processing_started": "OCR processing has been started. Please wait a moment and refresh.",
|
||||
"processing_complete": "OCR processing complete.",
|
||||
"processing_failed": "Failed to start OCR processing",
|
||||
"text_filtered_low_confidence": "OCR detected text with {{confidence}}% confidence, but it was discarded because your minimum threshold is {{threshold}}%.",
|
||||
"open_media_settings": "Open Settings",
|
||||
"view_extracted_text": "View extracted text (OCR)"
|
||||
},
|
||||
"command_palette": {
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.toast.no-title {
|
||||
.toast.no-title .toast-main-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toast.no-title .toast-icon {
|
||||
@@ -40,22 +41,26 @@
|
||||
}
|
||||
|
||||
.toast.no-title .toast-body {
|
||||
padding-inline-start: 0;
|
||||
padding-inline-end: 0;
|
||||
flex: 1;
|
||||
padding-block: var(--bs-toast-padding-y);
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.toast.no-title .toast-header {
|
||||
background-color: unset !important;
|
||||
.toast.no-title .toast-close {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: var(--bs-toast-padding-y) var(--bs-toast-padding-x);
|
||||
}
|
||||
|
||||
.toast {
|
||||
.toast-buttons {
|
||||
padding: 0 1em 1em 1em;
|
||||
padding: 0 var(--bs-toast-padding-x) var(--bs-toast-padding-y) var(--bs-toast-padding-x);
|
||||
display: flex;
|
||||
gap: 1em;
|
||||
justify-content: space-between;
|
||||
flex-direction: column;
|
||||
gap: 0.5em;
|
||||
|
||||
.btn {
|
||||
width: 100%;
|
||||
color: var(--bs-toast-color);
|
||||
background: var(--modal-control-button-background);
|
||||
|
||||
|
||||
@@ -42,21 +42,24 @@ function Toast({ id, title, timeout, progress, message, icon, buttons }: ToastOp
|
||||
id={`toast-${id}`}
|
||||
>
|
||||
{title ? (
|
||||
<div class="toast-header">
|
||||
<strong class="me-auto">
|
||||
{toastIcon}
|
||||
<span class="toast-title">{title}</span>
|
||||
</strong>
|
||||
{closeButton}
|
||||
</div>
|
||||
<>
|
||||
<div class="toast-header">
|
||||
<strong class="me-auto">
|
||||
{toastIcon}
|
||||
<span class="toast-title">{title}</span>
|
||||
</strong>
|
||||
{closeButton}
|
||||
</div>
|
||||
<div className="toast-body">{message}</div>
|
||||
</>
|
||||
) : (
|
||||
<div class="toast-icon">{toastIcon}</div>
|
||||
<div class="toast-main-row">
|
||||
<div class="toast-icon">{toastIcon}</div>
|
||||
<div className="toast-body">{message}</div>
|
||||
<div class="toast-close">{closeButton}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="toast-body">{message}</div>
|
||||
|
||||
{!title && <div class="toast-header">{closeButton}</div>}
|
||||
|
||||
{buttons && (
|
||||
<div class="toast-buttons">
|
||||
{buttons.map(({ text, onClick }) => (
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import "./ReadOnlyTextRepresentation.css";
|
||||
|
||||
import type { TextRepresentationResponse } from "@triliumnext/commons";
|
||||
import type { OCRProcessResponse, TextRepresentationResponse } from "@triliumnext/commons";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import appContext from "../../components/app_context";
|
||||
import { t } from "../../services/i18n";
|
||||
import server from "../../services/server";
|
||||
import toast from "../../services/toast";
|
||||
import { randomString } from "../../services/utils";
|
||||
import { TypeWidgetProps } from "./type_widget";
|
||||
|
||||
type State =
|
||||
@@ -62,10 +64,35 @@ export function TextRepresentation({ textUrl, processUrl }: TextRepresentationPr
|
||||
async function processOCR() {
|
||||
setProcessing(true);
|
||||
try {
|
||||
const response = await server.post<{ success: boolean; message?: string }>(processUrl, { forceReprocess: true });
|
||||
const response = await server.post<OCRProcessResponse>(processUrl, { forceReprocess: true });
|
||||
if (response.success) {
|
||||
toast.showMessage(t("ocr.processing_started"));
|
||||
setTimeout(fetchText, 2000);
|
||||
const result = response.result;
|
||||
const minConfidence = response.minConfidence ?? 0;
|
||||
|
||||
// Check if text was filtered due to low confidence
|
||||
if (result && !result.text && result.confidence > 0 && minConfidence > 0) {
|
||||
const confidencePercent = Math.round(result.confidence * 100);
|
||||
const thresholdPercent = Math.round(minConfidence * 100);
|
||||
toast.showPersistent({
|
||||
id: `ocr-low-confidence-${randomString(8)}`,
|
||||
icon: "bx bx-info-circle",
|
||||
message: t("ocr.text_filtered_low_confidence", {
|
||||
confidence: confidencePercent,
|
||||
threshold: thresholdPercent
|
||||
}),
|
||||
timeout: 15000,
|
||||
buttons: [{
|
||||
text: t("ocr.open_media_settings"),
|
||||
onClick: ({ dismissToast }) => {
|
||||
appContext.tabManager.openInNewTab("_optionsMedia", null, true);
|
||||
dismissToast();
|
||||
}
|
||||
}]
|
||||
});
|
||||
} else {
|
||||
toast.showMessage(t("ocr.processing_complete"));
|
||||
}
|
||||
setTimeout(fetchText, 500);
|
||||
} else {
|
||||
toast.showError(response.message || t("ocr.processing_failed"));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ async function main() {
|
||||
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
|
||||
|
||||
// Copy node modules dependencies
|
||||
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote" ]);
|
||||
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "@electron/remote", "tesseract.js" ]);
|
||||
build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css");
|
||||
|
||||
build.buildFrontend();
|
||||
|
||||
@@ -11,7 +11,7 @@ async function main() {
|
||||
build.copy("/packages/share-theme/src/templates", "share-theme/templates/");
|
||||
|
||||
// Copy node modules dependencies
|
||||
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path" ]);
|
||||
build.copyNodeModules([ "better-sqlite3", "bindings", "file-uri-to-path", "tesseract.js" ]);
|
||||
build.copy("/node_modules/ckeditor5/dist/ckeditor5-content.css", "ckeditor5-content.css");
|
||||
|
||||
build.buildFrontend();
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { TextRepresentationResponse } from "@triliumnext/commons";
|
||||
import type { OCRProcessResponse, TextRepresentationResponse } from "@triliumnext/commons";
|
||||
import type { Request } from "express";
|
||||
|
||||
import becca from "../../becca/becca.js";
|
||||
import ocrService from "../../services/ocr/ocr_service.js";
|
||||
import options from "../../services/options.js";
|
||||
import sql from "../../services/sql.js";
|
||||
|
||||
function getMinConfidenceThreshold(): number {
|
||||
const minConfidence = options.getOption('ocrMinConfidence') ?? 0;
|
||||
return parseFloat(minConfidence);
|
||||
}
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
* /api/ocr/process-note/{noteId}:
|
||||
@@ -48,7 +54,7 @@ import sql from "../../services/sql.js";
|
||||
* - session: []
|
||||
* tags: ["ocr"]
|
||||
*/
|
||||
async function processNoteOCR(req: Request<{ noteId: string }>) {
|
||||
async function processNoteOCR(req: Request<{ noteId: string }>): Promise<OCRProcessResponse | [number, OCRProcessResponse]> {
|
||||
const { noteId } = req.params;
|
||||
const { language, forceReprocess = false } = req.body || {};
|
||||
|
||||
@@ -62,7 +68,11 @@ async function processNoteOCR(req: Request<{ noteId: string }>) {
|
||||
return [400, { success: false, message: 'Note is not an image or has unsupported format' }];
|
||||
}
|
||||
|
||||
return { success: true, result };
|
||||
return {
|
||||
success: true,
|
||||
result,
|
||||
minConfidence: getMinConfidenceThreshold()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,7 +118,7 @@ async function processNoteOCR(req: Request<{ noteId: string }>) {
|
||||
* - session: []
|
||||
* tags: ["ocr"]
|
||||
*/
|
||||
async function processAttachmentOCR(req: Request<{ attachmentId: string }>) {
|
||||
async function processAttachmentOCR(req: Request<{ attachmentId: string }>): Promise<OCRProcessResponse | [number, OCRProcessResponse]> {
|
||||
const { attachmentId } = req.params;
|
||||
const { language, forceReprocess = false } = req.body || {};
|
||||
|
||||
@@ -122,7 +132,11 @@ async function processAttachmentOCR(req: Request<{ attachmentId: string }>) {
|
||||
return [400, { success: false, message: 'Attachment is not an image or has unsupported format' }];
|
||||
}
|
||||
|
||||
return { success: true, result };
|
||||
return {
|
||||
success: true,
|
||||
result,
|
||||
minConfidence: getMinConfidenceThreshold()
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,7 @@ import build from "./build.js";
|
||||
import dataDir from "./data_dir.js";
|
||||
|
||||
const APP_DB_VERSION = 236;
|
||||
const SYNC_VERSION = 37;
|
||||
const SYNC_VERSION = 38;
|
||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||
|
||||
export default {
|
||||
|
||||
Reference in New Issue
Block a user