diff --git a/apps/client/src/stylesheets/theme-next/shell.css b/apps/client/src/stylesheets/theme-next/shell.css index 5920ef0e95..3fa23b05a9 100644 --- a/apps/client/src/stylesheets/theme-next/shell.css +++ b/apps/client/src/stylesheets/theme-next/shell.css @@ -1259,6 +1259,12 @@ body.layout-horizontal #rest-pane > .classic-toolbar-widget { #center-pane .note-split { padding-top: 2px; 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 { diff --git a/apps/client/src/types-pdfjs.d.ts b/apps/client/src/types-pdfjs.d.ts new file mode 100644 index 0000000000..54a440f162 --- /dev/null +++ b/apps/client/src/types-pdfjs.d.ts @@ -0,0 +1,3 @@ +interface Window { + TRILIUM_VIEW_HISTORY_STORE?: object; +} diff --git a/apps/client/src/widgets/collections/NoteList.tsx b/apps/client/src/widgets/collections/NoteList.tsx index 5626cc33cf..ec80e6b197 100644 --- a/apps/client/src/widgets/collections/NoteList.tsx +++ b/apps/client/src/widgets/collections/NoteList.tsx @@ -11,7 +11,8 @@ import froca from "../../services/froca"; import { subscribeToMessages, unsubscribeToMessage as unsubscribeFromMessage } from "../../services/ws"; import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent } from "../react/hooks"; import { allViewTypes, ViewModeMedia, ViewModeProps, ViewTypeOptions } from "./interface"; -import ViewModeStorage from "./view_mode_storage"; +import ViewModeStorage, { type ViewModeStorageType } from "./view_mode_storage"; + interface NoteListProps { note: FNote | null | undefined; notePath: string | null | undefined; @@ -215,7 +216,7 @@ export function useNoteIds(note: FNote | null | undefined, viewType: ViewTypeOpt return noteIds; } -export function useViewModeConfig(note: FNote | null | undefined, viewType: ViewTypeOptions | undefined) { +export function useViewModeConfig(note: FNote | null | undefined, viewType: ViewModeStorageType | undefined) { const [ viewConfig, setViewConfig ] = useState<{ config: T | undefined; storeFn: (data: T) => void; diff --git a/apps/client/src/widgets/collections/view_mode_storage.ts b/apps/client/src/widgets/collections/view_mode_storage.ts index 95c3ff8004..b8c3b94f25 100644 --- a/apps/client/src/widgets/collections/view_mode_storage.ts +++ b/apps/client/src/widgets/collections/view_mode_storage.ts @@ -4,14 +4,16 @@ import { ViewTypeOptions } from "../collections/interface"; const ATTACHMENT_ROLE = "viewConfig"; +export type ViewModeStorageType = ViewTypeOptions | "pdfHistory"; + export default class ViewModeStorage { private note: FNote; private attachmentName: string; - constructor(note: FNote, viewType: ViewTypeOptions) { + constructor(note: FNote, viewType: ViewModeStorageType) { this.note = note; - this.attachmentName = viewType + ".json"; + this.attachmentName = `${viewType}.json`; } async store(data: T) { diff --git a/apps/client/src/widgets/containers/split_note_container.ts b/apps/client/src/widgets/containers/split_note_container.ts index 1cee46b73b..c006775a4e 100644 --- a/apps/client/src/widgets/containers/split_note_container.ts +++ b/apps/client/src/widgets/containers/split_note_container.ts @@ -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 type BasicWidget from "../basic_widget.js"; import Component from "../../components/component.js"; +import NoteContext from "../../components/note_context.js"; import splitService from "../../services/resizer.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 { hasBeenAlreadyShown?: boolean; @@ -74,7 +75,7 @@ export default class SplitNoteContainer extends FlexContainer { const subContexts = activeContext.getSubContexts(); - let noteContext: NoteContext | undefined = undefined; + let noteContext: NoteContext | undefined; if (isMobile() && subContexts.length > 1) { noteContext = subContexts.find(s => s.ntxId !== ntxId); } @@ -201,6 +202,11 @@ export default class SplitNoteContainer extends FlexContainer { async refresh() { 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 @@ -239,16 +245,16 @@ export default class SplitNoteContainer extends FlexContainer { widget.hasBeenAlreadyShown = true; return [widget.handleEvent("noteSwitched", noteSwitchedContext), this.refreshNotShown(noteSwitchedContext)]; - } else { - return Promise.resolve(); } + return Promise.resolve(); + } if (name === "activeContextChanged") { return this.refreshNotShown(data as EventData<"activeContextChanged">); - } else { - return super.handleEventInChildren(name, data); } + return super.handleEventInChildren(name, data); + } refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) { diff --git a/apps/client/src/widgets/type_widgets/File.tsx b/apps/client/src/widgets/type_widgets/File.tsx index 1871b96c52..0d5b038608 100644 --- a/apps/client/src/widgets/type_widgets/File.tsx +++ b/apps/client/src/widgets/type_widgets/File.tsx @@ -16,7 +16,7 @@ export default function FileTypeWidget({ note, parentComponent, noteContext }: T if (blob?.content) { return ; } else if (note.mime === "application/pdf") { - return ; + return noteContext && ; } else if (note.mime.startsWith("video/")) { return ; } else if (note.mime.startsWith("audio/")) { diff --git a/apps/client/src/widgets/type_widgets/file/Pdf.tsx b/apps/client/src/widgets/type_widgets/file/Pdf.tsx index 7b784865f5..e46d64717a 100644 --- a/apps/client/src/widgets/type_widgets/file/Pdf.tsx +++ b/apps/client/src/widgets/type_widgets/file/Pdf.tsx @@ -1,6 +1,7 @@ import { RefObject } from "preact"; import { useCallback, useEffect, useRef } from "preact/hooks"; +import appContext from "../../../components/app_context"; import type NoteContext from "../../../components/note_context"; import FBlob from "../../../entities/fblob"; import FNote from "../../../entities/fnote"; @@ -16,9 +17,9 @@ const VARIABLE_WHITELIST = new Set([ ]); export default function PdfPreview({ note, blob, componentId, noteContext }: { - note: FNote, - noteContext: NoteContext - blob: FBlob | null | undefined, + note: FNote; + noteContext: NoteContext; + blob: FBlob | null | undefined; componentId: string | undefined; }) { const iframeRef = useRef(null); @@ -150,9 +151,28 @@ export default function PdfPreview({ note, blob, componentId, noteContext }: { } }, [ 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 &&