mirror of
https://github.com/zadam/trilium.git
synced 2025-11-16 18:25:51 +01:00
Merge branch 'TriliumNext:develop' into develop
This commit is contained in:
@@ -8,11 +8,10 @@ import passwordEncryptionService from "./encryption/password_encryption.js";
|
||||
import config from "./config.js";
|
||||
import passwordService from "./encryption/password.js";
|
||||
import type { NextFunction, Request, Response } from 'express';
|
||||
import { AppRequest } from '../routes/route-interface.js';
|
||||
|
||||
const noAuthentication = config.General && config.General.noAuthentication === true;
|
||||
|
||||
function checkAuth(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkAuth(req: Request, res: Response, next: NextFunction) {
|
||||
if (!sqlInit.isDbInitialized()) {
|
||||
res.redirect("setup");
|
||||
}
|
||||
@@ -26,7 +25,7 @@ function checkAuth(req: AppRequest, res: Response, next: NextFunction) {
|
||||
|
||||
// for electron things which need network stuff
|
||||
// currently, we're doing that for file upload because handling form data seems to be difficult
|
||||
function checkApiAuthOrElectron(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkApiAuthOrElectron(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.session.loggedIn && !utils.isElectron() && !noAuthentication) {
|
||||
reject(req, res, "Logged in session not found");
|
||||
}
|
||||
@@ -35,7 +34,7 @@ function checkApiAuthOrElectron(req: AppRequest, res: Response, next: NextFuncti
|
||||
}
|
||||
}
|
||||
|
||||
function checkApiAuth(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkApiAuth(req: Request, res: Response, next: NextFunction) {
|
||||
if (!req.session.loggedIn && !noAuthentication) {
|
||||
reject(req, res, "Logged in session not found");
|
||||
}
|
||||
@@ -44,7 +43,7 @@ function checkApiAuth(req: AppRequest, res: Response, next: NextFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkAppInitialized(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkAppInitialized(req: Request, res: Response, next: NextFunction) {
|
||||
if (!sqlInit.isDbInitialized()) {
|
||||
res.redirect("setup");
|
||||
}
|
||||
@@ -53,7 +52,7 @@ function checkAppInitialized(req: AppRequest, res: Response, next: NextFunction)
|
||||
}
|
||||
}
|
||||
|
||||
function checkPasswordSet(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkPasswordSet(req: Request, res: Response, next: NextFunction) {
|
||||
if (!utils.isElectron() && !passwordService.isPasswordSet()) {
|
||||
res.redirect("set-password");
|
||||
} else {
|
||||
@@ -61,7 +60,7 @@ function checkPasswordSet(req: AppRequest, res: Response, next: NextFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
function checkPasswordNotSet(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkPasswordNotSet(req: Request, res: Response, next: NextFunction) {
|
||||
if (!utils.isElectron() && passwordService.isPasswordSet()) {
|
||||
res.redirect("login");
|
||||
} else {
|
||||
@@ -69,7 +68,7 @@ function checkPasswordNotSet(req: AppRequest, res: Response, next: NextFunction)
|
||||
}
|
||||
}
|
||||
|
||||
function checkAppNotInitialized(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkAppNotInitialized(req: Request, res: Response, next: NextFunction) {
|
||||
if (sqlInit.isDbInitialized()) {
|
||||
reject(req, res, "App already initialized.");
|
||||
}
|
||||
@@ -78,7 +77,7 @@ function checkAppNotInitialized(req: AppRequest, res: Response, next: NextFuncti
|
||||
}
|
||||
}
|
||||
|
||||
function checkEtapiToken(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkEtapiToken(req: Request, res: Response, next: NextFunction) {
|
||||
if (etapiTokenService.isValidAuthHeader(req.headers.authorization)) {
|
||||
next();
|
||||
}
|
||||
@@ -87,7 +86,7 @@ function checkEtapiToken(req: AppRequest, res: Response, next: NextFunction) {
|
||||
}
|
||||
}
|
||||
|
||||
function reject(req: AppRequest, res: Response, message: string) {
|
||||
function reject(req: Request, res: Response, message: string) {
|
||||
log.info(`${req.method} ${req.path} rejected with 401 ${message}`);
|
||||
|
||||
res.setHeader("Content-Type", "text/plain")
|
||||
@@ -95,7 +94,7 @@ function reject(req: AppRequest, res: Response, message: string) {
|
||||
.send(message);
|
||||
}
|
||||
|
||||
function checkCredentials(req: AppRequest, res: Response, next: NextFunction) {
|
||||
function checkCredentials(req: Request, res: Response, next: NextFunction) {
|
||||
if (!sqlInit.isDbInitialized()) {
|
||||
res.setHeader("Content-Type", "text/plain")
|
||||
.status(400)
|
||||
@@ -111,6 +110,13 @@ function checkCredentials(req: AppRequest, res: Response, next: NextFunction) {
|
||||
}
|
||||
|
||||
const header = req.headers['trilium-cred'] || '';
|
||||
if (typeof header !== "string") {
|
||||
res.setHeader("Content-Type", "text/plain")
|
||||
.status(400)
|
||||
.send('Invalid data type for trilium-cred.');
|
||||
return;
|
||||
}
|
||||
|
||||
const auth = Buffer.from(header, 'base64').toString();
|
||||
const colonIndex = auth.indexOf(':');
|
||||
const password = colonIndex === -1 ? "" : auth.substr(colonIndex + 1);
|
||||
|
||||
@@ -9,6 +9,7 @@ import config from "./config.js";
|
||||
import axios from "axios";
|
||||
import dayjs from "dayjs";
|
||||
import xml2js from "xml2js";
|
||||
import * as cheerio from 'cheerio';
|
||||
import cloningService from "./cloning.js";
|
||||
import appInfo from "./app_info.js";
|
||||
import searchService from "./search/services/search.js";
|
||||
@@ -94,6 +95,12 @@ interface Api {
|
||||
*/
|
||||
|
||||
xml2js: typeof xml2js;
|
||||
|
||||
/**
|
||||
* cheerio library for HTML parsing and manipulation. See {@link https://cheerio.js.org} for documentation
|
||||
*/
|
||||
|
||||
cheerio: typeof cheerio;
|
||||
|
||||
/**
|
||||
* Instance name identifies particular Trilium instance. It can be useful for scripts
|
||||
@@ -397,6 +404,7 @@ function BackendScriptApi(this: Api, currentNote: BNote, apiParams: ApiParams) {
|
||||
this.axios = axios;
|
||||
this.dayjs = dayjs;
|
||||
this.xml2js = xml2js;
|
||||
this.cheerio = cheerio;
|
||||
this.getInstanceName = () => config.General ? config.General.instanceName : null;
|
||||
this.getNote = noteId => becca.getNote(noteId);
|
||||
this.getBranch = branchId => becca.getBranch(branchId);
|
||||
|
||||
@@ -8,6 +8,7 @@ export default [
|
||||
{ type: 'label', name: 'disableInclusion' },
|
||||
{ type: 'label', name: 'appCss' },
|
||||
{ type: 'label', name: 'appTheme' },
|
||||
{ type: 'label', name: 'appThemeBase' },
|
||||
{ type: 'label', name: 'hidePromotedAttributes' },
|
||||
{ type: 'label', name: 'readOnly' },
|
||||
{ type: 'label', name: 'autoReadOnlyDisabled' },
|
||||
|
||||
@@ -54,7 +54,7 @@ enum Command {
|
||||
* duplicate subtrees. This way, all instances will generate the same structure with the same IDs.
|
||||
*/
|
||||
|
||||
let HIDDEN_SUBTREE_DEFINITION = buildHiddenSubtreeDefinition();
|
||||
let hiddenSubtreeDefinition: Item;
|
||||
|
||||
function buildHiddenSubtreeDefinition(): Item {
|
||||
return {
|
||||
@@ -288,11 +288,11 @@ function checkHiddenSubtree(force = false, extraOpts: CheckHiddenExtraOpts = {})
|
||||
return;
|
||||
}
|
||||
|
||||
if (force) {
|
||||
HIDDEN_SUBTREE_DEFINITION = buildHiddenSubtreeDefinition();
|
||||
if (!hiddenSubtreeDefinition || force) {
|
||||
hiddenSubtreeDefinition = buildHiddenSubtreeDefinition();
|
||||
}
|
||||
|
||||
checkHiddenSubtreeRecursively('root', HIDDEN_SUBTREE_DEFINITION, extraOpts);
|
||||
checkHiddenSubtreeRecursively('root', hiddenSubtreeDefinition, extraOpts);
|
||||
}
|
||||
|
||||
function checkHiddenSubtreeRecursively(parentNoteId: string, item: Item, extraOpts: CheckHiddenExtraOpts = {}) {
|
||||
|
||||
@@ -52,13 +52,15 @@ function sanitize(dirtyHtml: string) {
|
||||
return sanitizeHtml(dirtyHtml, {
|
||||
allowedTags,
|
||||
allowedAttributes: {
|
||||
'*': [ 'class', 'style', 'title', 'src', 'href', 'hash', 'disabled', 'align', 'alt', 'center', 'data-*' ]
|
||||
"*": [ 'class', 'style', 'title', 'src', 'href', 'hash', 'disabled', 'align', 'alt', 'center', 'data-*' ],
|
||||
"input": [ "type", "checked" ]
|
||||
},
|
||||
// Be consistent with `allowedSchemes` in `src\public\app\services\link.js`
|
||||
allowedSchemes: [
|
||||
'http', 'https', 'ftp', 'ftps', 'mailto', 'data', 'evernote', 'file', 'facetime', 'irc', 'gemini', 'git',
|
||||
'http', 'https', 'ftp', 'ftps', 'mailto', 'data', 'evernote', 'file', 'facetime', 'gemini', 'git',
|
||||
'gopher', 'imap', 'irc', 'irc6', 'jabber', 'jar', 'lastfm', 'ldap', 'ldaps', 'magnet', 'message',
|
||||
'mumble', 'nfs', 'onenote', 'pop', 'rmi', 's3', 'sftp', 'skype', 'sms', 'spotify', 'steam', 'svn', 'udp',
|
||||
'view-source', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack'
|
||||
'view-source', 'vlc', 'vnc', 'ws', 'wss', 'xmpp', 'jdbc', 'slack', 'tel', 'smb', 'zotero'
|
||||
],
|
||||
nonTextTags: [
|
||||
'head'
|
||||
|
||||
@@ -6,7 +6,7 @@ import protectedSessionService from "./protected_session.js";
|
||||
import noteService from "./notes.js";
|
||||
import optionService from "./options.js";
|
||||
import sql from "./sql.js";
|
||||
import jimp from "jimp";
|
||||
import { Jimp } from "jimp";
|
||||
import imageType from "image-type";
|
||||
import sanitizeFilename from "sanitize-filename";
|
||||
import isSvg from "is-svg";
|
||||
@@ -15,7 +15,7 @@ import htmlSanitizer from "./html_sanitizer.js";
|
||||
|
||||
async function processImage(uploadBuffer: Buffer, originalName: string, shrinkImageSwitch: boolean) {
|
||||
const compressImages = optionService.getOptionBool("compressImages");
|
||||
const origImageFormat = getImageType(uploadBuffer);
|
||||
const origImageFormat = await getImageType(uploadBuffer);
|
||||
|
||||
if (!origImageFormat || !["jpg", "png"].includes(origImageFormat.ext)) {
|
||||
shrinkImageSwitch = false;
|
||||
@@ -30,7 +30,7 @@ async function processImage(uploadBuffer: Buffer, originalName: string, shrinkIm
|
||||
|
||||
if (compressImages && shrinkImageSwitch) {
|
||||
finalImageBuffer = await shrinkImage(uploadBuffer, originalName);
|
||||
imageFormat = getImageType(finalImageBuffer);
|
||||
imageFormat = await getImageType(finalImageBuffer);
|
||||
} else {
|
||||
finalImageBuffer = uploadBuffer;
|
||||
imageFormat = origImageFormat || {
|
||||
@@ -44,16 +44,12 @@ async function processImage(uploadBuffer: Buffer, originalName: string, shrinkIm
|
||||
};
|
||||
}
|
||||
|
||||
function getImageType(buffer: Buffer) {
|
||||
if (isSvg(buffer)) {
|
||||
return {
|
||||
ext: 'svg'
|
||||
}
|
||||
async function getImageType(buffer: Buffer) {
|
||||
if (isSvg(buffer.toString())) {
|
||||
return { ext: 'svg' }
|
||||
}
|
||||
else {
|
||||
return imageType(buffer) || {
|
||||
ext: "jpg"
|
||||
}; // optimistic JPG default
|
||||
return await imageType(buffer) || { ext: "jpg" }; // optimistic JPG default
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,21 +208,19 @@ async function resize(buffer: Buffer, quality: number) {
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
const image = await jimp.read(buffer);
|
||||
const image = await Jimp.read(buffer);
|
||||
|
||||
if (image.bitmap.width > image.bitmap.height && image.bitmap.width > imageMaxWidthHeight) {
|
||||
image.resize(imageMaxWidthHeight, jimp.AUTO);
|
||||
image.resize({ w: imageMaxWidthHeight });
|
||||
}
|
||||
else if (image.bitmap.height > imageMaxWidthHeight) {
|
||||
image.resize(jimp.AUTO, imageMaxWidthHeight);
|
||||
image.resize({ h: imageMaxWidthHeight });
|
||||
}
|
||||
|
||||
image.quality(quality);
|
||||
|
||||
// when converting PNG to JPG, we lose the alpha channel, this is replaced by white to match Trilium white background
|
||||
image.background(0xFFFFFFFF);
|
||||
image.background = 0xFFFFFFFF;
|
||||
|
||||
const resultBuffer = await image.getBufferAsync(jimp.MIME_JPEG);
|
||||
const resultBuffer = await image.getBuffer("image/jpeg", { quality });
|
||||
|
||||
log.info(`Resizing image of ${resultBuffer.byteLength} took ${Date.now() - start}ms`);
|
||||
|
||||
|
||||
@@ -134,9 +134,11 @@ const defaultOptions: DefaultOption[] = [
|
||||
|
||||
// Text note configuration
|
||||
{ name: "textNoteEditorType", value: "ckeditor-balloon", isSynced: true },
|
||||
{ name: "textNoteEditorMultilineToolbar", value: "false", isSynced: true },
|
||||
|
||||
// HTML import configuration
|
||||
{ name: "layoutOrientation", value: "vertical", isSynced: false },
|
||||
{ name: "backgroundEffects", value: "false", isSynced: false },
|
||||
{ name: "allowedHtmlTags", value: JSON.stringify([
|
||||
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
|
||||
'li', 'b', 'i', 'strong', 'em', 'strike', 's', 'del', 'abbr', 'code', 'hr', 'br', 'div',
|
||||
|
||||
@@ -9,6 +9,8 @@ class SearchContext {
|
||||
includeArchivedNotes: boolean;
|
||||
includeHiddenNotes: boolean;
|
||||
ignoreHoistedNote: boolean;
|
||||
/** Whether to ignore certain attributes from the search such as ~internalLink. */
|
||||
ignoreInternalAttributes: boolean;
|
||||
ancestorNoteId?: string;
|
||||
ancestorDepth?: string;
|
||||
orderBy?: string;
|
||||
@@ -28,6 +30,7 @@ class SearchContext {
|
||||
this.includeArchivedNotes = !!params.includeArchivedNotes;
|
||||
this.includeHiddenNotes = !!params.includeHiddenNotes;
|
||||
this.ignoreHoistedNote = !!params.ignoreHoistedNote;
|
||||
this.ignoreInternalAttributes = !!params.ignoreInternalAttributes;
|
||||
this.ancestorNoteId = params.ancestorNoteId;
|
||||
|
||||
if (!this.ancestorNoteId && !this.ignoreHoistedNote) {
|
||||
|
||||
@@ -83,7 +83,7 @@ function getExpression(tokens: TokenData[], searchContext: SearchContext, level
|
||||
return `"${startIndex !== 0 ? "..." : ""}${searchContext.originalQuery.substr(startIndex, endIndex - startIndex)}${endIndex !== searchContext.originalQuery.length ? "..." : ""}"`;
|
||||
}
|
||||
|
||||
function resolveConstantOperand() {
|
||||
const resolveConstantOperand = () => {
|
||||
const operand = tokens[i];
|
||||
|
||||
if (!operand.inQuotes
|
||||
@@ -136,7 +136,7 @@ function getExpression(tokens: TokenData[], searchContext: SearchContext, level
|
||||
return date.format(format);
|
||||
}
|
||||
|
||||
function parseNoteProperty(): Expression | undefined | null {
|
||||
const parseNoteProperty: () => Expression | undefined | null = () => {
|
||||
if (tokens[i].token !== '.') {
|
||||
searchContext.addError('Expected "." to separate field path');
|
||||
return;
|
||||
|
||||
@@ -346,6 +346,7 @@ function searchNotesForAutocomplete(query: string, fastSearch: boolean = true) {
|
||||
includeArchivedNotes: false,
|
||||
includeHiddenNotes: true,
|
||||
fuzzyAttributeSearch: true,
|
||||
ignoreInternalAttributes: true,
|
||||
ancestorNoteId: hoistedNoteService.isHoistedInHiddenSubtree()
|
||||
? 'root'
|
||||
: hoistedNoteService.getHoistedNoteId()
|
||||
@@ -355,7 +356,7 @@ function searchNotesForAutocomplete(query: string, fastSearch: boolean = true) {
|
||||
|
||||
const trimmed = allSearchResults.slice(0, 200);
|
||||
|
||||
highlightSearchResults(trimmed, searchContext.highlightedTokens);
|
||||
highlightSearchResults(trimmed, searchContext.highlightedTokens, searchContext.ignoreInternalAttributes);
|
||||
|
||||
return trimmed.map(result => {
|
||||
return {
|
||||
@@ -367,7 +368,10 @@ function searchNotesForAutocomplete(query: string, fastSearch: boolean = true) {
|
||||
});
|
||||
}
|
||||
|
||||
function highlightSearchResults(searchResults: SearchResult[], highlightedTokens: string[]) {
|
||||
/**
|
||||
* @param ignoreInternalAttributes whether to ignore certain attributes from the search such as ~internalLink.
|
||||
*/
|
||||
function highlightSearchResults(searchResults: SearchResult[], highlightedTokens: string[], ignoreInternalAttributes = false) {
|
||||
highlightedTokens = Array.from(new Set(highlightedTokens));
|
||||
|
||||
// we remove < signs because they can cause trouble in matching and overwriting existing highlighted chunks
|
||||
@@ -395,6 +399,10 @@ function highlightSearchResults(searchResults: SearchResult[], highlightedTokens
|
||||
}
|
||||
|
||||
for (const attr of note.getAttributes()) {
|
||||
if (attr.type === "relation" && attr.name === "internalLink" && ignoreInternalAttributes) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (highlightedTokens.find(token => utils.normalize(attr.name).includes(token)
|
||||
|| utils.normalize(attr.value).includes(token))) {
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ export interface SearchParams {
|
||||
includeArchivedNotes?: boolean;
|
||||
includeHiddenNotes?: boolean;
|
||||
ignoreHoistedNote?: boolean;
|
||||
/** Whether to ignore certain attributes from the search such as ~internalLink. */
|
||||
ignoreInternalAttributes?: boolean;
|
||||
ancestorNoteId?: string;
|
||||
ancestorDepth?: string;
|
||||
orderBy?: string;
|
||||
|
||||
@@ -347,7 +347,6 @@ export default {
|
||||
/**
|
||||
* Get single value from the given query - first column from first returned row.
|
||||
*
|
||||
* @method
|
||||
* @param query - SQL query with ? used as parameter placeholder
|
||||
* @param params - array of params if needed
|
||||
* @returns single value
|
||||
@@ -357,7 +356,6 @@ export default {
|
||||
/**
|
||||
* Get first returned row.
|
||||
*
|
||||
* @method
|
||||
* @param query - SQL query with ? used as parameter placeholder
|
||||
* @param params - array of params if needed
|
||||
* @returns - map of column name to column value
|
||||
@@ -368,7 +366,6 @@ export default {
|
||||
/**
|
||||
* Get all returned rows.
|
||||
*
|
||||
* @method
|
||||
* @param query - SQL query with ? used as parameter placeholder
|
||||
* @param params - array of params if needed
|
||||
* @returns - array of all rows, each row is a map of column name to column value
|
||||
@@ -381,7 +378,6 @@ export default {
|
||||
/**
|
||||
* Get a map of first column mapping to second column.
|
||||
*
|
||||
* @method
|
||||
* @param query - SQL query with ? used as parameter placeholder
|
||||
* @param params - array of params if needed
|
||||
* @returns - map of first column to second column
|
||||
@@ -391,7 +387,6 @@ export default {
|
||||
/**
|
||||
* Get a first column in an array.
|
||||
*
|
||||
* @method
|
||||
* @param query - SQL query with ? used as parameter placeholder
|
||||
* @param params - array of params if needed
|
||||
* @returns array of first column of all returned rows
|
||||
@@ -401,7 +396,6 @@ export default {
|
||||
/**
|
||||
* Execute SQL
|
||||
*
|
||||
* @method
|
||||
* @param query - SQL query with ? used as parameter placeholder
|
||||
* @param params - array of params if needed
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@ import sqlInit from "./sql_init.js";
|
||||
import cls from "./cls.js";
|
||||
import keyboardActionsService from "./keyboard_actions.js";
|
||||
import remoteMain from "@electron/remote/main/index.js";
|
||||
import { App, BrowserWindow, WebContents, ipcMain } from 'electron';
|
||||
import { App, BrowserWindow, BrowserWindowConstructorOptions, WebContents, ipcMain } from 'electron';
|
||||
|
||||
import { fileURLToPath } from "url";
|
||||
import { dirname } from "path";
|
||||
@@ -31,7 +31,7 @@ async function createExtraWindow(extraWindowHash: string) {
|
||||
contextIsolation: false,
|
||||
spellcheck: spellcheckEnabled
|
||||
},
|
||||
frame: optionService.getOptionBool('nativeTitleBarVisible'),
|
||||
...getWindowExtraOpts(),
|
||||
icon: getIcon()
|
||||
});
|
||||
|
||||
@@ -71,6 +71,8 @@ async function createMainWindow(app: App) {
|
||||
|
||||
const { BrowserWindow } = (await import('electron')); // should not be statically imported
|
||||
|
||||
|
||||
|
||||
mainWindow = new BrowserWindow({
|
||||
x: mainWindowState.x,
|
||||
y: mainWindowState.y,
|
||||
@@ -82,9 +84,9 @@ async function createMainWindow(app: App) {
|
||||
contextIsolation: false,
|
||||
spellcheck: spellcheckEnabled,
|
||||
webviewTag: true
|
||||
},
|
||||
frame: optionService.getOptionBool('nativeTitleBarVisible'),
|
||||
icon: getIcon()
|
||||
},
|
||||
icon: getIcon(),
|
||||
...getWindowExtraOpts()
|
||||
});
|
||||
|
||||
mainWindowState.manage(mainWindow);
|
||||
@@ -110,6 +112,33 @@ async function createMainWindow(app: App) {
|
||||
});
|
||||
}
|
||||
|
||||
function getWindowExtraOpts() {
|
||||
const extraOpts: Partial<BrowserWindowConstructorOptions> = {};
|
||||
|
||||
const isMac = (process.platform === "darwin");
|
||||
const isWindows = (process.platform === "win32");
|
||||
|
||||
if (!optionService.getOptionBool('nativeTitleBarVisible')) {
|
||||
if (isMac) {
|
||||
extraOpts.titleBarStyle = "hiddenInset";
|
||||
extraOpts.titleBarOverlay = true;
|
||||
} else if (isWindows) {
|
||||
extraOpts.titleBarStyle = "hidden";
|
||||
extraOpts.titleBarOverlay = true;
|
||||
} else {
|
||||
// Linux or other platforms.
|
||||
extraOpts.frame = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Window effects (Mica)
|
||||
if (optionService.getOptionBool('backgroundEffects') && isWindows) {
|
||||
extraOpts.backgroundMaterial = "auto";
|
||||
}
|
||||
|
||||
return extraOpts;
|
||||
}
|
||||
|
||||
function configureWebContents(webContents: WebContents, spellcheckEnabled: boolean) {
|
||||
remoteMain.enable(webContents);
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ if (env.isDev()) {
|
||||
const debounce = (await import("debounce")).default;
|
||||
const debouncedReloadFrontend = debounce(() => reloadFrontend("source code change"), 200);
|
||||
chokidar
|
||||
.watch('src/public')
|
||||
.watch(utils.isElectron() ? 'dist/src/public' : 'src/public')
|
||||
.on('add', debouncedReloadFrontend)
|
||||
.on('change', debouncedReloadFrontend)
|
||||
.on('unlink', debouncedReloadFrontend);
|
||||
|
||||
Reference in New Issue
Block a user