parent
744e2df5a0
commit
e93e78f158
|
|
@ -79,8 +79,12 @@ import {
|
|||
linkAnnotationToTarget,
|
||||
updateNoteLinkRelation,
|
||||
} from "./utils/relation";
|
||||
import { getWorkspaceByTabID, getWorkspaceByUID } from "./utils/workspace";
|
||||
|
||||
const workspace = {};
|
||||
const workspace = {
|
||||
getWorkspaceByTabID,
|
||||
getWorkspaceByUID,
|
||||
};
|
||||
|
||||
const sync = {
|
||||
isSyncNote,
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
import { config } from "../../../package.json";
|
||||
import { PluginCEBase } from "../base";
|
||||
import { DetailsPane } from "./detailsPane";
|
||||
|
||||
export class ContextPane extends PluginCEBase {
|
||||
_item?: Zotero.Item;
|
||||
|
||||
_details!: any;
|
||||
_details!: DetailsPane;
|
||||
_sidenav: any;
|
||||
|
||||
get item() {
|
||||
|
|
@ -31,7 +32,7 @@ export class ContextPane extends PluginCEBase {
|
|||
}
|
||||
|
||||
init(): void {
|
||||
this._details = this._queryID("container");
|
||||
this._details = this._queryID("container") as unknown as DetailsPane;
|
||||
this._sidenav = this._queryID("sidenav");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,30 @@
|
|||
import { config } from "../../../package.json";
|
||||
import {
|
||||
getPrefJSON,
|
||||
registerPrefObserver,
|
||||
setPref,
|
||||
unregisterPrefObserver,
|
||||
} from "../../utils/prefs";
|
||||
|
||||
const ItemDetails = document.createXULElement("item-details")
|
||||
.constructor! as any;
|
||||
|
||||
const persistKey = "persist.workspaceContext";
|
||||
|
||||
export class DetailsPane extends ItemDetails {
|
||||
_prefObserverID!: symbol;
|
||||
|
||||
get pinnedPane() {
|
||||
// @ts-ignore super
|
||||
return super.pinnedPane;
|
||||
}
|
||||
|
||||
set pinnedPane(val) {
|
||||
// @ts-ignore super
|
||||
super.pinnedPane = val;
|
||||
this._persistState();
|
||||
}
|
||||
|
||||
content = MozXULElement.parseXULToFragment(`
|
||||
<linkset>
|
||||
<html:link
|
||||
|
|
@ -25,13 +47,54 @@ export class DetailsPane extends ItemDetails {
|
|||
init() {
|
||||
MozXULElement.insertFTLIfNeeded(`${config.addonRef}-notePreview.ftl`);
|
||||
MozXULElement.insertFTLIfNeeded(`${config.addonRef}-noteRelation.ftl`);
|
||||
|
||||
this._prefObserverID = registerPrefObserver(
|
||||
persistKey,
|
||||
this._restoreState.bind(this),
|
||||
);
|
||||
super.init();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
unregisterPrefObserver(this._prefObserverID);
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
render() {
|
||||
super.render();
|
||||
this._restoreState();
|
||||
}
|
||||
|
||||
forceUpdateSideNav() {
|
||||
this._sidenav
|
||||
.querySelectorAll("toolbarbutton")
|
||||
.forEach((elem: HTMLElement) => (elem.parentElement!.hidden = true));
|
||||
super.forceUpdateSideNav();
|
||||
}
|
||||
|
||||
_restorePinnedPane() {}
|
||||
|
||||
_persistState() {
|
||||
let state = getPrefJSON(persistKey);
|
||||
|
||||
if (state?.pinnedPane === this.pinnedPane) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = {
|
||||
...state,
|
||||
pinnedPane: this.pinnedPane,
|
||||
};
|
||||
|
||||
setPref(persistKey, JSON.stringify(state));
|
||||
}
|
||||
|
||||
_restoreState() {
|
||||
const state = getPrefJSON(persistKey);
|
||||
|
||||
console.trace("Restore State", state);
|
||||
|
||||
this.pinnedPane = state?.pinnedPane;
|
||||
this.scrollToPane(this.pinnedPane);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,17 @@ import { formatPath } from "../../utils/str";
|
|||
import { waitUtilAsync } from "../../utils/wait";
|
||||
import { OutlineType } from "../../utils/workspace";
|
||||
import { PluginCEBase } from "../base";
|
||||
import { getPref } from "../../utils/prefs";
|
||||
import {
|
||||
getPref,
|
||||
getPrefJSON,
|
||||
registerPrefObserver,
|
||||
setPref,
|
||||
unregisterPrefObserver,
|
||||
} from "../../utils/prefs";
|
||||
import { showHintWithLink } from "../../utils/hint";
|
||||
|
||||
const persistKey = "persist.workspaceOutline";
|
||||
|
||||
export class OutlinePane extends PluginCEBase {
|
||||
_outlineType: OutlineType = OutlineType.empty;
|
||||
_item?: Zotero.Item;
|
||||
|
|
@ -15,6 +23,8 @@ export class OutlinePane extends PluginCEBase {
|
|||
_outlineContainer!: HTMLIFrameElement;
|
||||
_notifierID!: string;
|
||||
|
||||
_prefObserverID!: symbol;
|
||||
|
||||
static outlineSources = [
|
||||
"",
|
||||
`chrome://${config.addonRef}/content/treeView.html`,
|
||||
|
|
@ -108,6 +118,7 @@ export class OutlinePane extends PluginCEBase {
|
|||
}
|
||||
|
||||
this._outlineType = newType;
|
||||
this._persistState();
|
||||
}
|
||||
|
||||
get item() {
|
||||
|
|
@ -139,9 +150,15 @@ export class OutlinePane extends PluginCEBase {
|
|||
["item"],
|
||||
"attachmentsBox",
|
||||
);
|
||||
|
||||
this._prefObserverID = registerPrefObserver(
|
||||
persistKey,
|
||||
this._restoreState.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
unregisterPrefObserver(this._prefObserverID);
|
||||
Zotero.Notifier.unregisterObserver(this._notifierID);
|
||||
this._outlineContainer.contentWindow?.removeEventListener(
|
||||
"message",
|
||||
|
|
@ -165,6 +182,7 @@ export class OutlinePane extends PluginCEBase {
|
|||
}
|
||||
|
||||
async render() {
|
||||
this._restoreState();
|
||||
if (this.outlineType === OutlineType.empty) {
|
||||
this.outlineType = OutlineType.treeView;
|
||||
}
|
||||
|
|
@ -380,4 +398,30 @@ export class OutlinePane extends PluginCEBase {
|
|||
return;
|
||||
}
|
||||
};
|
||||
|
||||
_persistState() {
|
||||
let state = getPrefJSON(persistKey);
|
||||
|
||||
if (state?.outlineType === this.outlineType) {
|
||||
return;
|
||||
}
|
||||
|
||||
state = {
|
||||
...state,
|
||||
outlineType: this.outlineType,
|
||||
};
|
||||
|
||||
setPref(persistKey, JSON.stringify(state));
|
||||
}
|
||||
|
||||
_restoreState() {
|
||||
const state = getPrefJSON(persistKey);
|
||||
if (
|
||||
typeof state.outlineType === "number" &&
|
||||
state.outlineType !== this.outlineType
|
||||
) {
|
||||
this.outlineType = state.outlineType;
|
||||
this.updateOutline();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,26 @@
|
|||
import { config } from "../../../package.json";
|
||||
import { getPrefJSON, registerPrefObserver, setPref, unregisterPrefObserver } from "../../utils/prefs";
|
||||
import { waitUtilAsync } from "../../utils/wait";
|
||||
import { PluginCEBase } from "../base";
|
||||
import { ContextPane } from "./contextPane";
|
||||
import { OutlinePane } from "./outlinePane";
|
||||
|
||||
const persistKey = "persist.workspace";
|
||||
|
||||
export class Workspace extends PluginCEBase {
|
||||
uid: string = Zotero.Utilities.randomString(8);
|
||||
_item?: Zotero.Item;
|
||||
|
||||
_prefObserverID!: symbol;
|
||||
|
||||
_editorElement!: EditorElement;
|
||||
_outline!: OutlinePane;
|
||||
_editorContainer!: XUL.Box;
|
||||
_context!: ContextPane;
|
||||
|
||||
_leftSplitter!: XUL.Splitter;
|
||||
_rightSplitter!: XUL.Splitter;
|
||||
|
||||
resizeOb!: ResizeObserver;
|
||||
|
||||
get content() {
|
||||
|
|
@ -77,11 +86,23 @@ export class Workspace extends PluginCEBase {
|
|||
this._addon.data.workspace.instances[this.uid] = new WeakRef(this);
|
||||
|
||||
this._outline = this._queryID("left-container") as unknown as OutlinePane;
|
||||
|
||||
this._editorContainer = this._queryID("center-container") as XUL.Box;
|
||||
this._editorElement = this._queryID("editor-main") as EditorElement;
|
||||
this._outline._editorElement = this._editorElement;
|
||||
|
||||
this._context = this._queryID("right-container") as unknown as ContextPane;
|
||||
|
||||
this._leftSplitter = this._queryID("left-splitter") as XUL.Splitter;
|
||||
this._rightSplitter = this._queryID("right-splitter") as XUL.Splitter;
|
||||
|
||||
this._leftSplitter.addEventListener("mouseup", () => {
|
||||
this._persistState();
|
||||
});
|
||||
this._rightSplitter.addEventListener("mouseup", () => {
|
||||
this._persistState();
|
||||
});
|
||||
|
||||
this.resizeOb = new ResizeObserver(() => {
|
||||
if (!this.editor) return;
|
||||
this._addon.api.editor.scroll(
|
||||
|
|
@ -90,9 +111,15 @@ export class Workspace extends PluginCEBase {
|
|||
);
|
||||
});
|
||||
this.resizeOb.observe(this._editorElement);
|
||||
|
||||
this._prefObserverID = registerPrefObserver(
|
||||
persistKey,
|
||||
this._restoreState.bind(this),
|
||||
);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
unregisterPrefObserver(this._prefObserverID);
|
||||
this.resizeOb.disconnect();
|
||||
delete this._addon.data.workspace.instances[this.uid];
|
||||
}
|
||||
|
|
@ -101,6 +128,8 @@ export class Workspace extends PluginCEBase {
|
|||
await this._outline.render();
|
||||
await this.updateEditor();
|
||||
await this._context.render();
|
||||
|
||||
this._restoreState();
|
||||
}
|
||||
|
||||
async updateEditor() {
|
||||
|
|
@ -125,4 +154,34 @@ export class Workspace extends PluginCEBase {
|
|||
this._addon.api.editor.scrollToSection(this.editor, options.sectionName);
|
||||
}
|
||||
}
|
||||
|
||||
_persistState() {
|
||||
const state = {
|
||||
leftState: this._leftSplitter.getAttribute("state"),
|
||||
rightState: this._rightSplitter.getAttribute("state"),
|
||||
leftWidth: window.getComputedStyle(this._outline)?.width,
|
||||
centerWidth: window.getComputedStyle(this._editorContainer)?.width,
|
||||
rightWidth: window.getComputedStyle(this._context)?.width,
|
||||
};
|
||||
setPref(persistKey, JSON.stringify(state));
|
||||
}
|
||||
|
||||
_restoreState() {
|
||||
const state = getPrefJSON(persistKey);
|
||||
if (typeof state.leftState === "string") {
|
||||
this._leftSplitter.setAttribute("state", state.leftState);
|
||||
}
|
||||
if (typeof state.rightState === "string") {
|
||||
this._rightSplitter.setAttribute("state", state.rightState);
|
||||
}
|
||||
if (state.leftWidth) {
|
||||
this._outline.style.width = state.leftWidth;
|
||||
}
|
||||
if (state.centerWidth) {
|
||||
this._editorContainer.style.width = state.centerWidth;
|
||||
}
|
||||
if (state.rightWidth) {
|
||||
this._context.style.width = state.rightWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { Workspace } from "../../elements/workspace/workspace";
|
||||
import { waitUtilAsync } from "../../utils/wait";
|
||||
|
||||
export async function initWorkspace(container: XUL.Box, item: Zotero.Item) {
|
||||
|
|
@ -14,7 +15,7 @@ export async function initWorkspace(container: XUL.Box, item: Zotero.Item) {
|
|||
|
||||
await waitUtilAsync(() => !!customElements.get("bn-workspace"));
|
||||
|
||||
const workspace = new (customElements.get("bn-workspace")!)() as any;
|
||||
const workspace = new (customElements.get("bn-workspace")!)() as Workspace;
|
||||
container.append(workspace);
|
||||
workspace.item = item;
|
||||
workspace.containerType = "tab";
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export async function openWorkspaceTab(
|
|||
onClose: () => {},
|
||||
});
|
||||
const workspace = await initWorkspace(container, item);
|
||||
workspace.scrollEditorTo({
|
||||
workspace?.scrollEditorTo({
|
||||
lineIndex,
|
||||
sectionName,
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,10 +19,9 @@ export async function openWorkspaceWindow(
|
|||
"#workspace-container",
|
||||
) as XUL.Box;
|
||||
const workspace = await addon.hooks.onInitWorkspace(container, item);
|
||||
workspace.scrollEditorTo(options);
|
||||
workspace?.scrollEditorTo(options);
|
||||
|
||||
win.focus();
|
||||
// @ts-ignore
|
||||
win.updateTitle();
|
||||
return win;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,3 +11,27 @@ export function setPref(key: string, value: string | number | boolean) {
|
|||
export function clearPref(key: string) {
|
||||
return Zotero.Prefs.clear(`${config.prefsPrefix}.${key}`, true);
|
||||
}
|
||||
|
||||
export function getPrefJSON(key: string) {
|
||||
try {
|
||||
return JSON.parse(String(getPref(key) || "{}"));
|
||||
} catch (e) {
|
||||
setPref(key, "{}");
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
export function registerPrefObserver(
|
||||
key: string,
|
||||
callback: (value: any) => void,
|
||||
) {
|
||||
return Zotero.Prefs.registerObserver(
|
||||
`${config.prefsPrefix}.${key}`,
|
||||
callback,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
export function unregisterPrefObserver(observerID: symbol) {
|
||||
return Zotero.Prefs.unregisterObserver(observerID);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,33 @@
|
|||
export enum OutlineType {
|
||||
import { Workspace } from "../elements/workspace/workspace";
|
||||
|
||||
export { getWorkspaceByTabID, getWorkspaceByUID, OutlineType };
|
||||
|
||||
enum OutlineType {
|
||||
empty = 0,
|
||||
treeView,
|
||||
mindMap,
|
||||
bubbleMap,
|
||||
}
|
||||
|
||||
export function getWorkspaceByUID(uid: string): HTMLElement | undefined {
|
||||
function getWorkspaceByUID(uid: string): Workspace | undefined {
|
||||
const workspace = addon.data.workspace.instances[uid]?.deref();
|
||||
if (!workspace?.ownerDocument) {
|
||||
delete addon.data.workspace.instances[uid];
|
||||
return undefined;
|
||||
}
|
||||
return workspace;
|
||||
return workspace as Workspace;
|
||||
}
|
||||
|
||||
function getWorkspaceByTabID(tabID?: string): Workspace | undefined {
|
||||
const win = Zotero.getMainWindow();
|
||||
if (!tabID) {
|
||||
const _Zotero_Tabs = win.Zotero_Tabs as typeof Zotero_Tabs;
|
||||
if (_Zotero_Tabs.selectedType !== "note") return;
|
||||
tabID = Zotero_Tabs.selectedID;
|
||||
}
|
||||
const workspace = Zotero.getMainWindow().document.querySelector(
|
||||
`#${tabID} > bn-workspace`,
|
||||
);
|
||||
if (!workspace) return;
|
||||
return workspace as Workspace;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue