mirror of
https://github.com/zadam/trilium.git
synced 2026-01-06 14:44:25 +01:00
Merge remote-tracking branch 'origin/main' into feature/pdfjs_sidebar_experiments
Some checks are pending
Checks / main (push) Waiting to run
Some checks are pending
Checks / main (push) Waiting to run
This commit is contained in:
commit
7182d32d9c
@ -1259,6 +1259,12 @@ body.layout-horizontal #rest-pane > .classic-toolbar-widget {
|
|||||||
#center-pane .note-split {
|
#center-pane .note-split {
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
background-color: var(--note-split-background-color, var(--main-background-color));
|
background-color: var(--note-split-background-color, var(--main-background-color));
|
||||||
|
transition: border-color 250ms ease-in;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
border-color: var(--link-selection-outline-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
body:not(.background-effects) #center-pane .note-split {
|
body:not(.background-effects) #center-pane .note-split {
|
||||||
|
|||||||
3
apps/client/src/types-pdfjs.d.ts
vendored
Normal file
3
apps/client/src/types-pdfjs.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
interface Window {
|
||||||
|
TRILIUM_VIEW_HISTORY_STORE?: object;
|
||||||
|
}
|
||||||
@ -11,7 +11,8 @@ import froca from "../../services/froca";
|
|||||||
import { subscribeToMessages, unsubscribeToMessage as unsubscribeFromMessage } from "../../services/ws";
|
import { subscribeToMessages, unsubscribeToMessage as unsubscribeFromMessage } from "../../services/ws";
|
||||||
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent } from "../react/hooks";
|
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent } from "../react/hooks";
|
||||||
import { allViewTypes, ViewModeMedia, ViewModeProps, ViewTypeOptions } from "./interface";
|
import { allViewTypes, ViewModeMedia, ViewModeProps, ViewTypeOptions } from "./interface";
|
||||||
import ViewModeStorage from "./view_mode_storage";
|
import ViewModeStorage, { type ViewModeStorageType } from "./view_mode_storage";
|
||||||
|
|
||||||
interface NoteListProps {
|
interface NoteListProps {
|
||||||
note: FNote | null | undefined;
|
note: FNote | null | undefined;
|
||||||
notePath: string | null | undefined;
|
notePath: string | null | undefined;
|
||||||
@ -215,7 +216,7 @@ export function useNoteIds(note: FNote | null | undefined, viewType: ViewTypeOpt
|
|||||||
return noteIds;
|
return noteIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useViewModeConfig<T extends object>(note: FNote | null | undefined, viewType: ViewTypeOptions | undefined) {
|
export function useViewModeConfig<T extends object>(note: FNote | null | undefined, viewType: ViewModeStorageType | undefined) {
|
||||||
const [ viewConfig, setViewConfig ] = useState<{
|
const [ viewConfig, setViewConfig ] = useState<{
|
||||||
config: T | undefined;
|
config: T | undefined;
|
||||||
storeFn: (data: T) => void;
|
storeFn: (data: T) => void;
|
||||||
|
|||||||
@ -4,14 +4,16 @@ import { ViewTypeOptions } from "../collections/interface";
|
|||||||
|
|
||||||
const ATTACHMENT_ROLE = "viewConfig";
|
const ATTACHMENT_ROLE = "viewConfig";
|
||||||
|
|
||||||
|
export type ViewModeStorageType = ViewTypeOptions | "pdfHistory";
|
||||||
|
|
||||||
export default class ViewModeStorage<T extends object> {
|
export default class ViewModeStorage<T extends object> {
|
||||||
|
|
||||||
private note: FNote;
|
private note: FNote;
|
||||||
private attachmentName: string;
|
private attachmentName: string;
|
||||||
|
|
||||||
constructor(note: FNote, viewType: ViewTypeOptions) {
|
constructor(note: FNote, viewType: ViewModeStorageType) {
|
||||||
this.note = note;
|
this.note = note;
|
||||||
this.attachmentName = viewType + ".json";
|
this.attachmentName = `${viewType}.json`;
|
||||||
}
|
}
|
||||||
|
|
||||||
async store(data: T) {
|
async store(data: T) {
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import FlexContainer from "./flex_container.js";
|
|
||||||
import appContext, { type CommandData, type CommandListenerData, type EventData, type EventNames, type NoteSwitchedContext } from "../../components/app_context.js";
|
import appContext, { type CommandData, type CommandListenerData, type EventData, type EventNames, type NoteSwitchedContext } from "../../components/app_context.js";
|
||||||
import type BasicWidget from "../basic_widget.js";
|
|
||||||
import Component from "../../components/component.js";
|
import Component from "../../components/component.js";
|
||||||
|
import NoteContext from "../../components/note_context.js";
|
||||||
import splitService from "../../services/resizer.js";
|
import splitService from "../../services/resizer.js";
|
||||||
import { isMobile } from "../../services/utils.js";
|
import { isMobile } from "../../services/utils.js";
|
||||||
import NoteContext from "../../components/note_context.js";
|
import type BasicWidget from "../basic_widget.js";
|
||||||
|
import NoteContextAwareWidget from "../note_context_aware_widget.js";
|
||||||
|
import FlexContainer from "./flex_container.js";
|
||||||
|
|
||||||
interface SplitNoteWidget extends BasicWidget {
|
interface SplitNoteWidget extends BasicWidget {
|
||||||
hasBeenAlreadyShown?: boolean;
|
hasBeenAlreadyShown?: boolean;
|
||||||
@ -74,7 +75,7 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
|
|
||||||
|
|
||||||
const subContexts = activeContext.getSubContexts();
|
const subContexts = activeContext.getSubContexts();
|
||||||
let noteContext: NoteContext | undefined = undefined;
|
let noteContext: NoteContext | undefined;
|
||||||
if (isMobile() && subContexts.length > 1) {
|
if (isMobile() && subContexts.length > 1) {
|
||||||
noteContext = subContexts.find(s => s.ntxId !== ntxId);
|
noteContext = subContexts.find(s => s.ntxId !== ntxId);
|
||||||
}
|
}
|
||||||
@ -201,6 +202,11 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
this.toggleExt(true);
|
this.toggleExt(true);
|
||||||
|
|
||||||
|
// Mark the active note context.
|
||||||
|
for (const child of this.children as NoteContextAwareWidget[]) {
|
||||||
|
child.$widget.toggleClass("active", !!child.noteContext?.isActive());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleInt(show: boolean) {} // not needed
|
toggleInt(show: boolean) {} // not needed
|
||||||
@ -239,16 +245,16 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
widget.hasBeenAlreadyShown = true;
|
widget.hasBeenAlreadyShown = true;
|
||||||
|
|
||||||
return [widget.handleEvent("noteSwitched", noteSwitchedContext), this.refreshNotShown(noteSwitchedContext)];
|
return [widget.handleEvent("noteSwitched", noteSwitchedContext), this.refreshNotShown(noteSwitchedContext)];
|
||||||
} else {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name === "activeContextChanged") {
|
if (name === "activeContextChanged") {
|
||||||
return this.refreshNotShown(data as EventData<"activeContextChanged">);
|
return this.refreshNotShown(data as EventData<"activeContextChanged">);
|
||||||
} else {
|
|
||||||
return super.handleEventInChildren(name, data);
|
|
||||||
}
|
}
|
||||||
|
return super.handleEventInChildren(name, data);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) {
|
refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export default function FileTypeWidget({ note, parentComponent, noteContext }: T
|
|||||||
if (blob?.content) {
|
if (blob?.content) {
|
||||||
return <TextPreview content={blob.content} />;
|
return <TextPreview content={blob.content} />;
|
||||||
} else if (note.mime === "application/pdf") {
|
} else if (note.mime === "application/pdf") {
|
||||||
return <PdfPreview blob={blob} note={note} componentId={parentComponent?.componentId} noteContext={noteContext} />;
|
return noteContext && <PdfPreview blob={blob} note={note} componentId={parentComponent?.componentId} noteContext={noteContext} />;
|
||||||
} else if (note.mime.startsWith("video/")) {
|
} else if (note.mime.startsWith("video/")) {
|
||||||
return <VideoPreview note={note} />;
|
return <VideoPreview note={note} />;
|
||||||
} else if (note.mime.startsWith("audio/")) {
|
} else if (note.mime.startsWith("audio/")) {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { RefObject } from "preact";
|
import { RefObject } from "preact";
|
||||||
import { useCallback, useEffect, useRef } from "preact/hooks";
|
import { useCallback, useEffect, useRef } from "preact/hooks";
|
||||||
|
|
||||||
|
import appContext from "../../../components/app_context";
|
||||||
import type NoteContext from "../../../components/note_context";
|
import type NoteContext from "../../../components/note_context";
|
||||||
import FBlob from "../../../entities/fblob";
|
import FBlob from "../../../entities/fblob";
|
||||||
import FNote from "../../../entities/fnote";
|
import FNote from "../../../entities/fnote";
|
||||||
@ -16,9 +17,9 @@ const VARIABLE_WHITELIST = new Set([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export default function PdfPreview({ note, blob, componentId, noteContext }: {
|
export default function PdfPreview({ note, blob, componentId, noteContext }: {
|
||||||
note: FNote,
|
note: FNote;
|
||||||
noteContext: NoteContext
|
noteContext: NoteContext;
|
||||||
blob: FBlob | null | undefined,
|
blob: FBlob | null | undefined;
|
||||||
componentId: string | undefined;
|
componentId: string | undefined;
|
||||||
}) {
|
}) {
|
||||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||||
@ -150,9 +151,28 @@ export default function PdfPreview({ note, blob, componentId, noteContext }: {
|
|||||||
}
|
}
|
||||||
}, [ blob ]);
|
}, [ blob ]);
|
||||||
|
|
||||||
|
// Trigger focus when iframe content is clicked (iframe focus doesn't bubble)
|
||||||
|
useEffect(() => {
|
||||||
|
const iframe = iframeRef.current;
|
||||||
|
if (!iframe) return;
|
||||||
|
|
||||||
|
const handleIframeClick = () => {
|
||||||
|
if (noteContext.ntxId) {
|
||||||
|
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for clicks on the iframe's content window
|
||||||
|
const iframeDoc = iframe.contentWindow?.document;
|
||||||
|
if (iframeDoc) {
|
||||||
|
iframeDoc.addEventListener('click', handleIframeClick);
|
||||||
|
return () => iframeDoc.removeEventListener('click', handleIframeClick);
|
||||||
|
}
|
||||||
|
}, [ iframeRef.current?.contentWindow, noteContext ]);
|
||||||
|
|
||||||
return (historyConfig &&
|
return (historyConfig &&
|
||||||
<iframe
|
<iframe
|
||||||
|
tabIndex={300}
|
||||||
ref={iframeRef}
|
ref={iframeRef}
|
||||||
class="pdf-preview"
|
class="pdf-preview"
|
||||||
src={`pdfjs/web/viewer.html?file=../../api/notes/${note.noteId}/open&lang=${locale}&sidebar=${newLayout ? "0" : "1"}`}
|
src={`pdfjs/web/viewer.html?file=../../api/notes/${note.noteId}/open&lang=${locale}&sidebar=${newLayout ? "0" : "1"}`}
|
||||||
@ -201,7 +221,7 @@ function useStyleInjection(iframeRef: RefObject<HTMLIFrameElement>) {
|
|||||||
|
|
||||||
function getRootCssVariables() {
|
function getRootCssVariables() {
|
||||||
const styles = getComputedStyle(document.documentElement);
|
const styles = getComputedStyle(document.documentElement);
|
||||||
const vars = {};
|
const vars: Record<string, string> = {};
|
||||||
|
|
||||||
for (let i = 0; i < styles.length; i++) {
|
for (let i = 0; i < styles.length; i++) {
|
||||||
const prop = styles[i];
|
const prop = styles[i];
|
||||||
@ -213,7 +233,7 @@ function getRootCssVariables() {
|
|||||||
return vars;
|
return vars;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cssVarsToString(vars) {
|
function cssVarsToString(vars: Record<string, string>) {
|
||||||
return `:root {\n${Object.entries(vars)
|
return `:root {\n${Object.entries(vars)
|
||||||
.map(([k, v]) => ` ${k}: ${v};`)
|
.map(([k, v]) => ` ${k}: ${v};`)
|
||||||
.join('\n')}\n}`;
|
.join('\n')}\n}`;
|
||||||
|
|||||||
@ -4,8 +4,6 @@ import { setupPdfPages } from "./pages";
|
|||||||
import { setupPdfAttachments } from "./attachments";
|
import { setupPdfAttachments } from "./attachments";
|
||||||
import { setupPdfLayers } from "./layers";
|
import { setupPdfLayers } from "./layers";
|
||||||
|
|
||||||
const LOG_EVENT_BUS = false;
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
if (urlParams.get("sidebar") === "0") {
|
if (urlParams.get("sidebar") === "0") {
|
||||||
@ -20,9 +18,6 @@ async function main() {
|
|||||||
}
|
}
|
||||||
const app = window.PDFViewerApplication;
|
const app = window.PDFViewerApplication;
|
||||||
|
|
||||||
if (LOG_EVENT_BUS) {
|
|
||||||
patchEventBus();
|
|
||||||
}
|
|
||||||
app.eventBus.on("documentloaded", () => {
|
app.eventBus.on("documentloaded", () => {
|
||||||
manageSave();
|
manageSave();
|
||||||
extractAndSendToc();
|
extractAndSendToc();
|
||||||
@ -74,7 +69,7 @@ function manageSave() {
|
|||||||
window.parent.postMessage({
|
window.parent.postMessage({
|
||||||
type: "pdfjs-viewer-document-modified",
|
type: "pdfjs-viewer-document-modified",
|
||||||
data: data
|
data: data
|
||||||
}, "*");
|
}, window.location.origin);
|
||||||
storage.resetModified();
|
storage.resetModified();
|
||||||
timeout = null;
|
timeout = null;
|
||||||
}, 2_000);
|
}, 2_000);
|
||||||
@ -93,15 +88,4 @@ function manageSave() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function patchEventBus() {
|
|
||||||
const eventBus = window.PDFViewerApplication.eventBus;
|
|
||||||
const originalDispatch = eventBus.dispatch.bind(eventBus);
|
|
||||||
|
|
||||||
eventBus.dispatch = (type: string, data?: any) => {
|
|
||||||
console.log("PDF.js event:", type, data);
|
|
||||||
return originalDispatch(type, data);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
main();
|
main();
|
||||||
console.log("Custom script loaded");
|
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
export default function interceptViewHistory(customOptions?: object) {
|
export default function interceptViewHistory(customOptions?: object) {
|
||||||
|
// We need to monkey-patch the localStorage used by PDF.js to store view history.
|
||||||
|
// Other attempts to intercept the history saving/loading (like overriding methods on PDFViewerApplication) have failed.
|
||||||
const originalSetItem = Storage.prototype.setItem;
|
const originalSetItem = Storage.prototype.setItem;
|
||||||
Storage.prototype.setItem = function (key: string, value: string) {
|
Storage.prototype.setItem = function (key: string, value: string) {
|
||||||
if (key === "pdfjs.history") {
|
if (key === "pdfjs.history") {
|
||||||
@ -40,7 +42,7 @@ function saveHistory(value: string) {
|
|||||||
window.parent.postMessage({
|
window.parent.postMessage({
|
||||||
type: "pdfjs-viewer-save-view-history",
|
type: "pdfjs-viewer-save-view-history",
|
||||||
data: JSON.stringify(history)
|
data: JSON.stringify(history)
|
||||||
}, "*");
|
}, window.location.origin);
|
||||||
saveTimeout = null;
|
saveTimeout = null;
|
||||||
}, 2_000);
|
}, 2_000);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,7 +14,8 @@
|
|||||||
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
|
"tsBuildInfoFile": "dist/tsconfig.app.tsbuildinfo"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*.ts"
|
"src/**/*.ts",
|
||||||
|
"../../apps/client/src/types-pdfjs.d.ts"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"eslint.config.js",
|
"eslint.config.js",
|
||||||
@ -23,7 +24,7 @@
|
|||||||
],
|
],
|
||||||
"references": [
|
"references": [
|
||||||
{
|
{
|
||||||
"path": "../commons/tsconfig.app.json"
|
"path": "../commons/tsconfig.lib.json"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -63,6 +63,9 @@
|
|||||||
{
|
{
|
||||||
"path": "./packages/share-theme"
|
"path": "./packages/share-theme"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "./packages/pdfjs-viewer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "./scripts"
|
"path": "./scripts"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user