mirror of
https://github.com/uthvah/sync-embeds.git
synced 2026-07-22 16:30:22 +00:00
feat: remove uneeded changes, add delay for loading
This commit is contained in:
parent
d590ccd9d2
commit
8013a63e90
4 changed files with 40 additions and 126 deletions
|
|
@ -149,10 +149,8 @@ class EmbedManager {
|
|||
|
||||
// Check if we should load all embeds on page load
|
||||
if (this.plugin.settings.loadAllOnPageLoad) {
|
||||
// Load immediately without intersection observer
|
||||
requestAnimationFrame(() => {
|
||||
this.loadEmbed(embedContainer, file, section, displayAlias, ctx, placeholder, options);
|
||||
});
|
||||
await this.loadEmbed(embedContainer, file, section, displayAlias, ctx, placeholder, options);
|
||||
await new Promise(r => setTimeout(r, 50));
|
||||
} else {
|
||||
// Aggressive lazy loading to prevent scrollbar jumps
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
|
|
|
|||
45
src/main.js
45
src/main.js
|
|
@ -16,8 +16,6 @@ const DEFAULT_SETTINGS = {
|
|||
loadAllOnPageLoad: false,
|
||||
showFocusHighlight: true,
|
||||
showHeaderHints: true, // NEW: Header hints (enforcement is always on)
|
||||
hideSectionHeaders: false, // NEW: Hide headers in section embeds
|
||||
showPropertiesToggle: true, // NEW: Show properties collapse toggle button
|
||||
debugMode: false
|
||||
};
|
||||
|
||||
|
|
@ -34,11 +32,11 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
|
||||
async onload() {
|
||||
await this.loadSettings();
|
||||
|
||||
|
||||
// Initialize managers
|
||||
this.embedManager = new EmbedManager(this);
|
||||
this.commandInterceptor = new CommandInterceptor(this);
|
||||
|
||||
|
||||
// Setup command interception with monkey-around
|
||||
if (this.settings.enableCommandInterception) {
|
||||
this.setupCommandInterception();
|
||||
|
|
@ -90,26 +88,26 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
|
||||
// Add settings tab
|
||||
this.addSettingTab(new SyncEmbedsSettingTab(this.app, this));
|
||||
|
||||
|
||||
// Apply focus highlight setting
|
||||
this.updateFocusHighlight();
|
||||
|
||||
|
||||
// Log successful load
|
||||
this.log('Sync Embeds plugin loaded successfully');
|
||||
}
|
||||
|
||||
onunload() {
|
||||
this.log('Unloading Sync Embeds plugin');
|
||||
|
||||
|
||||
// Clean up command interception
|
||||
this.uninstallers.forEach(uninstall => uninstall());
|
||||
this.uninstallers = [];
|
||||
|
||||
|
||||
// Clean up managers
|
||||
if (this.embedManager) {
|
||||
this.embedManager.cleanup();
|
||||
}
|
||||
|
||||
|
||||
this.currentFocusedEmbed = null;
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +119,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
{ level: 5, name: 'Heading 5', key: '5' },
|
||||
{ level: 6, name: 'Heading 6', key: '6' },
|
||||
];
|
||||
|
||||
|
||||
headerLevels.forEach(({ level, name, key }) => {
|
||||
this.addCommand({
|
||||
id: `insert-header-${level}`,
|
||||
|
|
@ -129,13 +127,13 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
editorCallback: (editor, view) => {
|
||||
// Check if we're in a sync embed
|
||||
const focusedEmbed = this.getFocusedEmbed();
|
||||
|
||||
|
||||
if (focusedEmbed) {
|
||||
// Use our custom handler
|
||||
const handler = this.commandInterceptor.insertHeaderCommand(level);
|
||||
return handler(focusedEmbed);
|
||||
}
|
||||
|
||||
|
||||
// Fall back to normal behavior for non-embed editing
|
||||
this.commandInterceptor.insertHeader({ editor }, level);
|
||||
},
|
||||
|
|
@ -147,7 +145,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
]
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
this.log('Registered header commands with default hotkeys (Alt+2-6)');
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +157,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
const plugin = this;
|
||||
return function(command, ...args) {
|
||||
const focusedEmbed = plugin.getFocusedEmbed();
|
||||
|
||||
|
||||
// If embed is focused, check if we should intercept
|
||||
if (focusedEmbed) {
|
||||
// Check for our header commands first
|
||||
|
|
@ -170,17 +168,17 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
const handler = plugin.commandInterceptor.insertHeaderCommand(level);
|
||||
return handler(focusedEmbed);
|
||||
}
|
||||
|
||||
|
||||
// Check if we have a custom handler
|
||||
if (plugin.commandInterceptor.hasHandler(command.id)) {
|
||||
plugin.log(`Intercepting command: ${command.id}`);
|
||||
return plugin.commandInterceptor.handle(command.id, focusedEmbed, ...args);
|
||||
}
|
||||
|
||||
|
||||
// For commands we don't handle, let them execute on the embed's editor
|
||||
// This ensures ALL hotkeys work, including custom user-defined ones
|
||||
plugin.log(`Passing through command to embed: ${command.id}`);
|
||||
|
||||
|
||||
// Check if command has a callback that expects an editor
|
||||
if (command.editorCallback && focusedEmbed.editor) {
|
||||
try {
|
||||
|
|
@ -191,7 +189,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return old.call(this, command, ...args);
|
||||
};
|
||||
}
|
||||
|
|
@ -213,7 +211,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
}
|
||||
});
|
||||
this.uninstallers.push(getActiveViewUninstall);
|
||||
|
||||
|
||||
// Patch getActiveViewOfType on workspace.activeLeaf as well
|
||||
const getActiveLeafUninstall = around(this.app.workspace, {
|
||||
activeLeaf: {
|
||||
|
|
@ -231,7 +229,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
}
|
||||
});
|
||||
this.uninstallers.push(getActiveLeafUninstall);
|
||||
|
||||
|
||||
this.log('Command interception setup complete');
|
||||
} catch (error) {
|
||||
console.error('Sync Embeds: Failed to setup command interception:', error);
|
||||
|
|
@ -285,12 +283,7 @@ module.exports = class SyncEmbedPlugin extends Plugin {
|
|||
container.style.setProperty('--sync-max-height', this.settings.maxEmbedHeight);
|
||||
container.style.setProperty('--sync-gap', this.settings.gapBetweenEmbeds);
|
||||
});
|
||||
|
||||
// Update viewport CSS for section embeds (header visibility)
|
||||
if (this.embedManager) {
|
||||
this.embedManager.refreshViewportCSS();
|
||||
}
|
||||
|
||||
|
||||
this.log('Refreshed all embeds with new settings');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
|
||||
// === APPEARANCE SECTION ===
|
||||
containerEl.createEl('h3', { text: 'Appearance' });
|
||||
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Render as callout')
|
||||
.setDesc('Render embeds as callouts with sticky headers and collapse functionality')
|
||||
|
|
@ -148,16 +148,6 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Show properties toggle button')
|
||||
.setDesc('Display a toggle button to collapse/expand properties in embeds')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.showPropertiesToggle)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.showPropertiesToggle = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Show inline title')
|
||||
.setDesc('Display note title at the top of whole-note embeds (not applicable to section embeds or embeds with aliases)')
|
||||
|
|
@ -179,17 +169,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
}));
|
||||
|
||||
// === HEADER MANAGEMENT SECTION ===
|
||||
containerEl.createEl('h3', { text: 'Header Management' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Hide section headers')
|
||||
.setDesc('Hide the header when embedding a section under a header')
|
||||
.addToggle(toggle => toggle
|
||||
.setValue(this.plugin.settings.hideSectionHeaders)
|
||||
.onChange(async (value) => {
|
||||
this.plugin.settings.hideSectionHeaders = value;
|
||||
await this.plugin.saveSettings();
|
||||
}));
|
||||
containerEl.createEl('h3', { text: 'Header Management' });
|
||||
|
||||
new Setting(containerEl)
|
||||
.setName('Show header hints')
|
||||
|
|
@ -265,7 +245,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
<li><code>![[Note Name|Custom Title]]</code> - Display with custom title</li>
|
||||
<li><code>![[Note Name#Section|Custom Title]]</code> - Section with custom title</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p><strong>Per-Embed Custom Options:</strong></p>
|
||||
<ul>
|
||||
<li><code>![[Note|Alias{height:500px}]]</code> - Custom height for this embed</li>
|
||||
|
|
@ -276,7 +256,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
<li><code>![[Note|Alias{height:400px,title:false}]]</code> - Multiple options</li>
|
||||
</ul>
|
||||
<p><em>Note: Options go inside curly braces before the closing ]]</em></p>
|
||||
|
||||
|
||||
<p><strong>Dynamic Patterns:</strong></p>
|
||||
<ul>
|
||||
<li><code>![[Daily/{{date:YYYY-MM-DD}}|Today]]</code> - Current date with display name</li>
|
||||
|
|
@ -287,7 +267,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
<li><code>{{time:HH:mm}}</code> - Current time</li>
|
||||
<li><code>{{title}}</code> - Current note's title</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p><strong>Header Management:</strong></p>
|
||||
<ul>
|
||||
<li>Use <code>Alt+2</code> through <code>Alt+6</code> to insert headers (H2-H6)</li>
|
||||
|
|
@ -298,7 +278,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
<li>Whole-note embeds allow H1-H6 freely</li>
|
||||
<li>These hotkeys can be customized in Obsidian's Hotkeys settings</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p><strong>Date Format Examples:</strong></p>
|
||||
<ul>
|
||||
<li><code>YYYY-MM-DD</code> - 2024-03-15</li>
|
||||
|
|
@ -306,7 +286,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
<li><code>DD MMM YYYY</code> - 15 Mar 2024</li>
|
||||
<li><code>dddd, MMMM Do YYYY</code> - Friday, March 15th 2024</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p><strong>Complete Example:</strong></p>
|
||||
<pre><code>\`\`\`sync
|
||||
![[Daily Notes/{{date:YYYY-MM-DD}}|Today's Note]]
|
||||
|
|
@ -314,7 +294,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
![[Tasks#Inbox|My Tasks{height:300px}]]
|
||||
![[Projects/{{title}}#Notes|Project Notes{title:false}]]
|
||||
\`\`\`</code></pre>
|
||||
|
||||
|
||||
<p><strong>Tips:</strong></p>
|
||||
<ul>
|
||||
<li>Use aliases (text after <code>|</code>) with dynamic patterns for better display</li>
|
||||
|
|
@ -327,7 +307,7 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
<li>Multiple embeds of the same note/section are allowed</li>
|
||||
<li>Header hierarchy enforcement maintains document structure in section embeds</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<p><em>Note: Dynamic patterns are cached for 1 second to improve performance.</em></p>
|
||||
`;
|
||||
|
||||
|
|
@ -377,15 +357,15 @@ class SyncEmbedsSettingTab extends PluginSettingTab {
|
|||
validateCSSValue(value) {
|
||||
// Basic validation for CSS length values
|
||||
if (!value || value.trim() === '') return false;
|
||||
|
||||
|
||||
// Allow common CSS units
|
||||
const validPattern = /^(auto|none|\d+(\.\d+)?(px|em|rem|vh|vw|%))$/;
|
||||
const isValid = validPattern.test(value.trim());
|
||||
|
||||
|
||||
if (!isValid) {
|
||||
new Notice('Invalid CSS value. Use units like: px, em, rem, vh, vw, %, or "auto"/"none"');
|
||||
}
|
||||
|
||||
|
||||
return isValid;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class ViewportController {
|
|||
|
||||
applyViewportRestriction(embedData) {
|
||||
const { view } = embedData;
|
||||
|
||||
|
||||
const style = document.createElement('style');
|
||||
style.className = 'sync-viewport-style';
|
||||
|
||||
|
|
@ -47,7 +47,7 @@ class ViewportController {
|
|||
}
|
||||
|
||||
updateViewportCSS(embedData, style) {
|
||||
const { sectionInfo, embedId, propertiesLineCount = 0, file } = embedData;
|
||||
const { sectionInfo, embedId, file } = embedData;
|
||||
const { startLine, endLine } = sectionInfo;
|
||||
|
||||
// Frontmatter DOM Offset compensation
|
||||
|
|
@ -62,22 +62,14 @@ class ViewportController {
|
|||
const domStartLine = Math.max(0, startLine - domOffset);
|
||||
const domEndLine = Math.max(0, endLine - domOffset);
|
||||
|
||||
|
||||
let adjustedStartLine = domStartLine - propertiesLineCount;
|
||||
let adjustedEndLine = domEndLine - propertiesLineCount;
|
||||
|
||||
if (hideHeader) {
|
||||
adjustedStartLine += 1;
|
||||
}
|
||||
|
||||
const css = `
|
||||
/* Hide all lines BEFORE and INCLUDING the section header */
|
||||
[data-embed-id="${embedId}"] .cm-line:nth-child(-n+${adjustedStartLine + 1}) {
|
||||
[data-embed-id="${embedId}"] .cm-line:nth-child(-n+${domStartLine + 1}) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Hide all lines AFTER the section */
|
||||
[data-embed-id="${embedId}"] .cm-line:nth-child(n+${adjustedEndLine + 1}) {
|
||||
[data-embed-id="${embedId}"] .cm-line:nth-child(n+${domEndLine + 1}) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
|
|
@ -247,27 +239,7 @@ class ViewportController {
|
|||
setupContentConstraints(embedData) {
|
||||
const { view, editor, component } = embedData;
|
||||
let isProgrammaticUpdate = false;
|
||||
|
||||
// CRITICAL FIX: Update viewport IMMEDIATELY before visible changes
|
||||
const updateViewportImmediately = () => {
|
||||
if (!embedData.viewportActive) return;
|
||||
|
||||
const currentContent = editor.getValue();
|
||||
const newSectionInfo = this.findSectionBounds(currentContent, embedData.section);
|
||||
|
||||
if (newSectionInfo.startLine !== -1) {
|
||||
embedData.sectionInfo = newSectionInfo;
|
||||
|
||||
// recalculate properties line count on content change
|
||||
embedData.propertiesLineCount = this.countPropertiesLines(currentContent);
|
||||
|
||||
// Update CSS synchronously to prevent flashing
|
||||
if (embedData.viewportStyle) {
|
||||
this.updateViewportCSS(embedData, embedData.viewportStyle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Force cursor strictly inside bounds (preventing up/down arrow drifting)
|
||||
const enforceCursorBounds = () => {
|
||||
if (isProgrammaticUpdate || !embedData.viewportActive || !embedData.sectionInfo) return;
|
||||
|
|
@ -330,14 +302,14 @@ class ViewportController {
|
|||
const scrollTop = cmScroller.scrollTop;
|
||||
const lineHeight = editor.defaultTextHeight || 20;
|
||||
const firstVisibleLine = Math.floor(scrollTop / lineHeight);
|
||||
|
||||
|
||||
// Frontmatter DOM Offset compensation for scrolling bounds
|
||||
let domOffset = 0;
|
||||
const fileCache = this.plugin.app.metadataCache.getFileCache(embedData.file);
|
||||
if (fileCache && fileCache.frontmatterPosition) {
|
||||
domOffset = fileCache.frontmatterPosition.end.line;
|
||||
}
|
||||
|
||||
|
||||
const domStartLine = Math.max(0, embedData.sectionInfo.startLine - domOffset);
|
||||
const domEndLine = Math.max(0, embedData.sectionInfo.endLine - domOffset);
|
||||
|
||||
|
|
@ -416,35 +388,6 @@ class ViewportController {
|
|||
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
}
|
||||
|
||||
countPropertiesLines(content) {
|
||||
// Count the number of lines used by frontmatter/properties
|
||||
// Properties are wrapped in --- markers at the start of the file
|
||||
const lines = content.split('\n');
|
||||
|
||||
// Check if file starts with properties
|
||||
if (lines.length === 0 || lines[0] !== '---') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Find the closing --- marker
|
||||
let propertiesEndLine = -1;
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
if (lines[i] === '---') {
|
||||
propertiesEndLine = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (propertiesEndLine === -1) {
|
||||
// Invalid frontmatter, no closing marker
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return the number of lines including both --- markers
|
||||
// These lines are NOT rendered as .cm-line elements
|
||||
return propertiesEndLine + 1;
|
||||
}
|
||||
|
||||
cleanupViewport(embedData) {
|
||||
if (embedData.viewportStyle) {
|
||||
embedData.viewportStyle.remove();
|
||||
|
|
|
|||
Loading…
Reference in a new issue