mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Use Obsidian window-aware DOM APIs
This commit is contained in:
parent
f41bab3f69
commit
9fbaafac69
7 changed files with 62 additions and 31 deletions
10
src/main.ts
10
src/main.ts
|
|
@ -47,12 +47,10 @@ export default class CodexPanelPlugin extends Plugin {
|
|||
}
|
||||
|
||||
async activateView(): Promise<CodexPanelView> {
|
||||
const existing = this.app.workspace.getLeavesOfType(VIEW_TYPE_CODEX_PANEL)[0];
|
||||
const leaf = existing ?? this.app.workspace.getRightLeaf(false);
|
||||
if (!leaf) throw new Error("Could not create a right sidebar leaf.");
|
||||
|
||||
await leaf.setViewState({ type: VIEW_TYPE_CODEX_PANEL, active: true });
|
||||
await this.app.workspace.revealLeaf(leaf);
|
||||
const leaf = await this.app.workspace.ensureSideLeaf(VIEW_TYPE_CODEX_PANEL, "right", {
|
||||
active: true,
|
||||
reveal: true,
|
||||
});
|
||||
return leaf.view as CodexPanelView;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ export class PanelMessageRenderer {
|
|||
workspaceRoot: state.activeThreadCwd ?? this.options.vaultPath,
|
||||
openDetails: state.openDetails,
|
||||
onDetailsToggle: () => {
|
||||
window.requestAnimationFrame(() => {
|
||||
messagesEl.win.requestAnimationFrame(() => {
|
||||
state.messagesPinnedToBottom = isNearScrollBottom(messagesEl);
|
||||
});
|
||||
},
|
||||
|
|
@ -72,7 +72,7 @@ export class PanelMessageRenderer {
|
|||
});
|
||||
syncMessageRenderBlocks(messagesEl, blocks, this.options.blockSignatures);
|
||||
|
||||
window.requestAnimationFrame(() => {
|
||||
messagesEl.win.requestAnimationFrame(() => {
|
||||
if (shouldScrollToBottom) {
|
||||
messagesEl.scrollTop = bottomScrollTop(messagesEl);
|
||||
} else {
|
||||
|
|
@ -99,7 +99,7 @@ export class PanelMessageRenderer {
|
|||
if (!this.options.state.messagesPinnedToBottom) return;
|
||||
const messagesEl = parent.closest<HTMLElement>(".codex-panel__messages");
|
||||
if (!messagesEl) return;
|
||||
window.requestAnimationFrame(() => {
|
||||
messagesEl.win.requestAnimationFrame(() => {
|
||||
if (!this.options.state.messagesPinnedToBottom) return;
|
||||
messagesEl.scrollTop = bottomScrollTop(messagesEl);
|
||||
this.options.state.messagesPinnedToBottom = isNearScrollBottom(messagesEl);
|
||||
|
|
|
|||
|
|
@ -202,14 +202,14 @@ export class CodexPanelView extends ItemView {
|
|||
|
||||
async onOpen(): Promise<void> {
|
||||
this.composerController.registerNoteIndexInvalidation((eventRef) => this.registerEvent(eventRef));
|
||||
this.registerDomEvent(activeDocument, "pointerdown", (event) => this.closeToolbarPanelOnOutsidePointer(event));
|
||||
this.registerDomEvent(this.containerEl.doc, "pointerdown", (event) => this.closeToolbarPanelOnOutsidePointer(event));
|
||||
this.render();
|
||||
await this.ensureConnected();
|
||||
}
|
||||
|
||||
async onClose(): Promise<void> {
|
||||
if (this.scheduledRenderTimer !== null) {
|
||||
window.clearTimeout(this.scheduledRenderTimer);
|
||||
this.containerEl.win.clearTimeout(this.scheduledRenderTimer);
|
||||
this.scheduledRenderTimer = null;
|
||||
}
|
||||
this.connection.disconnect();
|
||||
|
|
@ -800,7 +800,8 @@ export class CodexPanelView extends ItemView {
|
|||
if (!this.hasOpenToolbarPanel()) return;
|
||||
|
||||
const target = event.target;
|
||||
if (target instanceof Element) {
|
||||
const viewWindow = this.containerEl.doc.defaultView;
|
||||
if (viewWindow && target instanceof viewWindow.Element) {
|
||||
const insideToolbarPanel = target.closest(".codex-panel__toolbar-primary, .codex-panel__toolbar-panel");
|
||||
if (insideToolbarPanel && this.containerEl.contains(insideToolbarPanel)) return;
|
||||
}
|
||||
|
|
@ -823,7 +824,7 @@ export class CodexPanelView extends ItemView {
|
|||
|
||||
private scheduleRender(): void {
|
||||
if (this.scheduledRenderTimer !== null) return;
|
||||
this.scheduledRenderTimer = window.setTimeout(() => {
|
||||
this.scheduledRenderTimer = this.containerEl.win.setTimeout(() => {
|
||||
this.scheduledRenderTimer = null;
|
||||
this.render();
|
||||
}, 50);
|
||||
|
|
|
|||
|
|
@ -104,19 +104,15 @@ export function syncComposerHeight(composer: HTMLTextAreaElement | null): void {
|
|||
if (!composer) return;
|
||||
const style = getComputedStyle(composer);
|
||||
const minHeight = parseCssPixels(style.minHeight, 76);
|
||||
const maxHeight = composerMaxHeight(style.maxHeight);
|
||||
setCssProps(composer, { height: "auto" });
|
||||
const maxHeight = composerMaxHeight(style.maxHeight, composer.win);
|
||||
const resetHeightProps: Record<string, string> = { height: "auto" };
|
||||
composer.setCssProps(resetHeightProps);
|
||||
const nextHeight = Math.min(Math.max(composer.scrollHeight, minHeight), maxHeight);
|
||||
setCssProps(composer, {
|
||||
const sizingProps: Record<string, string> = {
|
||||
height: `${nextHeight}px`,
|
||||
"overflow-y": composer.scrollHeight > maxHeight ? "auto" : "hidden",
|
||||
});
|
||||
}
|
||||
|
||||
function setCssProps(element: HTMLElement, props: Record<string, string>): void {
|
||||
for (const [key, value] of Object.entries(props)) {
|
||||
element.style.setProperty(key, value);
|
||||
}
|
||||
};
|
||||
composer.setCssProps(sizingProps);
|
||||
}
|
||||
|
||||
function parseCssPixels(value: string, fallback: number): number {
|
||||
|
|
@ -124,26 +120,26 @@ function parseCssPixels(value: string, fallback: number): number {
|
|||
return Number.isFinite(parsed) ? parsed : fallback;
|
||||
}
|
||||
|
||||
function composerMaxHeight(value: string): number {
|
||||
return parseCssLengthExpression(value) ?? Math.min(208, window.innerHeight * 0.4);
|
||||
function composerMaxHeight(value: string, win: Window): number {
|
||||
return parseCssLengthExpression(value, win) ?? Math.min(208, win.innerHeight * 0.4);
|
||||
}
|
||||
|
||||
function parseCssLengthExpression(value: string): number | null {
|
||||
function parseCssLengthExpression(value: string, win: Window): number | null {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed || trimmed === "none") return null;
|
||||
if (/^min\(/i.test(trimmed)) {
|
||||
const values = Array.from(trimmed.matchAll(/(-?\d+(?:\.\d+)?)\s*(px|vh)/gi), (match) =>
|
||||
cssLengthToPixels(Number.parseFloat(match[1]), match[2]),
|
||||
cssLengthToPixels(Number.parseFloat(match[1]), match[2], win),
|
||||
).filter((candidate): candidate is number => Number.isFinite(candidate));
|
||||
return values.length > 0 ? Math.min(...values) : null;
|
||||
}
|
||||
const length = trimmed.match(/^(-?\d+(?:\.\d+)?)\s*(px|vh)$/i);
|
||||
if (!length) return null;
|
||||
return cssLengthToPixels(Number.parseFloat(length[1]), length[2]);
|
||||
return cssLengthToPixels(Number.parseFloat(length[1]), length[2], win);
|
||||
}
|
||||
|
||||
function cssLengthToPixels(value: number, unit: string): number {
|
||||
if (unit.toLowerCase() === "vh") return (window.innerHeight * value) / 100;
|
||||
function cssLengthToPixels(value: number, unit: string, win: Window): number {
|
||||
if (unit.toLowerCase() === "vh") return (win.innerHeight * value) / 100;
|
||||
return value;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ function renderThreadRenameRow(parent: HTMLElement, thread: ToolbarThreadRow, ac
|
|||
actions.cancelRenameThread(thread.threadId);
|
||||
}
|
||||
};
|
||||
window.setTimeout(() => {
|
||||
input.win.setTimeout(() => {
|
||||
if (input.ownerDocument.activeElement !== input) {
|
||||
input.focus();
|
||||
input.select();
|
||||
|
|
|
|||
|
|
@ -226,6 +226,25 @@ export function setIcon(element: HTMLElement, icon: string): void {
|
|||
function ensureElementHelpers(): void {
|
||||
if (typeof HTMLElement === "undefined") return;
|
||||
|
||||
const NodeCtor = globalThis.Node ?? document.defaultView?.Node;
|
||||
if (NodeCtor && !Object.getOwnPropertyDescriptor(NodeCtor.prototype, "doc")) {
|
||||
Object.defineProperty(NodeCtor.prototype, "doc", {
|
||||
configurable: true,
|
||||
get(this: Node): Document {
|
||||
return this.ownerDocument ?? document;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (NodeCtor && !Object.getOwnPropertyDescriptor(NodeCtor.prototype, "win")) {
|
||||
Object.defineProperty(NodeCtor.prototype, "win", {
|
||||
configurable: true,
|
||||
get(this: Node): Window {
|
||||
return this.ownerDocument?.defaultView ?? window;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!HTMLElement.prototype.addClass) {
|
||||
HTMLElement.prototype.addClass = function addClass(className: string): void {
|
||||
this.classList.add(className);
|
||||
|
|
|
|||
|
|
@ -21,6 +21,23 @@ declare global {
|
|||
|
||||
export function installObsidianDomShims(): void {
|
||||
beforeEach(() => {
|
||||
const NodeCtor = globalThis.Node ?? document.defaultView?.Node;
|
||||
if (NodeCtor && !Object.getOwnPropertyDescriptor(NodeCtor.prototype, "doc")) {
|
||||
Object.defineProperty(NodeCtor.prototype, "doc", {
|
||||
configurable: true,
|
||||
get(this: Node): Document {
|
||||
return this.ownerDocument ?? document;
|
||||
},
|
||||
});
|
||||
}
|
||||
if (NodeCtor && !Object.getOwnPropertyDescriptor(NodeCtor.prototype, "win")) {
|
||||
Object.defineProperty(NodeCtor.prototype, "win", {
|
||||
configurable: true,
|
||||
get(this: Node): Window {
|
||||
return this.ownerDocument?.defaultView ?? window;
|
||||
},
|
||||
});
|
||||
}
|
||||
HTMLElement.prototype.addClass = function addClass(this: HTMLElement, className: string): void {
|
||||
this.classList.add(...className.split(/\s+/).filter(Boolean));
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue