Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions src/frontend/main/backlinks.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { WebpageData } from "src/shared/website-data";
import { DynamicInsertedFeature } from "src/shared/dynamic-inserted-feature";
import { BacklinksOptions } from "src/shared/features/backlinks";
import { InsertedFeature } from "src/shared/inserted-feature";

export class Backlink {
public backlinkEl: HTMLAnchorElement;
Expand Down Expand Up @@ -60,11 +61,90 @@ export class BacklinkList extends DynamicInsertedFeature<
});
}

protected getElementDefinitions() {
const definitions = super.getElementDefinitions();

// Add conditional class for sidebar placement
const currentClasses = definitions[InsertedFeature.CONTENT_KEY].className;
const baseClasses = Array.isArray(currentClasses)
? [...currentClasses]
: (currentClasses ? [currentClasses] : []);

// Add height constraint class when in right sidebar
if (this.options.placeInRightSidebar) {
baseClasses.push('backlinks-constrained');
}

definitions[InsertedFeature.CONTENT_KEY].className = baseClasses;

return definitions;
}

protected generateContent(container: HTMLElement) {
const deps = this.getDependencies();

this.backlinks = deps.backlinkPaths.map(
(url) => new Backlink(container, url)
);
}

protected onAfterMount(): void {
// Add CSS styles for height constraints
if (this.options.placeInRightSidebar && !document.getElementById('backlinks-height-styles')) {
const style = document.createElement('style');
style.id = 'backlinks-height-styles';
style.textContent = `
/* Backlinks height constraints when in right sidebar */
.backlinks-constrained {
max-height: 33.33vh;
overflow-y: auto;
overflow-x: hidden;
}

/* Ensure proper scrolling behavior */
.backlinks-constrained::-webkit-scrollbar {
width: 8px;
}

.backlinks-constrained::-webkit-scrollbar-track {
background: var(--background-secondary);
}

.backlinks-constrained::-webkit-scrollbar-thumb {
background: var(--text-muted);
border-radius: 4px;
}

.backlinks-constrained::-webkit-scrollbar-thumb:hover {
background: var(--text-accent);
}

/* Responsive adjustments */
@media (max-width: 768px) {
.backlinks-constrained {
max-height: 25vh;
}
}

@media (max-width: 480px) {
.backlinks-constrained {
max-height: 20vh;
}
}
`;
document.head.appendChild(style);
}
}

public destroy(): void {
super.destroy();

// Clean up dynamically added styles if no other constrained backlinks exist
if (document.querySelectorAll('.backlinks-constrained').length === 0) {
const styleEl = document.getElementById('backlinks-height-styles');
if (styleEl) {
styleEl.remove();
}
}
}
}
211 changes: 211 additions & 0 deletions src/frontend/main/frontmatter-properties.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import { DynamicInsertedFeature } from "src/shared/dynamic-inserted-feature";
import { InsertedFeatureOptions } from "src/shared/features/feature-options-base";

interface FrontmatterPropertiesDependencies {
properties: {[key: string]: any};
}

export class FrontmatterProperties extends DynamicInsertedFeature<
InsertedFeatureOptions,
FrontmatterPropertiesDependencies
> {
constructor(properties: {[key: string]: any}) {
super(ObsidianSite.metadata.featureOptions.frontmatterProperties, { properties });
}

protected generateContent(container: HTMLElement) {
const deps = this.getDependencies();

// Create a container for properties with compact design similar to tags
const propertiesContainer = document.createElement("div");
propertiesContainer.classList.add("frontmatter-properties-container");
container.appendChild(propertiesContainer);

if (!deps.properties || Object.keys(deps.properties).length === 0) {
const emptyMessage = document.createElement("div");
emptyMessage.classList.add("frontmatter-properties-empty");
emptyMessage.textContent = "No properties";
propertiesContainer.appendChild(emptyMessage);
return;
}

// Display properties as compact items, similar to tags design
for (const [key, value] of Object.entries(deps.properties)) {
const propertyItem = document.createElement("div");
propertyItem.classList.add("frontmatter-property");

const keyElement = document.createElement("span");
keyElement.classList.add("frontmatter-property-key");
keyElement.textContent = key + ":";
propertyItem.appendChild(keyElement);

const valueElement = document.createElement("div");
valueElement.classList.add("frontmatter-property-value");

// Handle different value types with link support
if (value === null || value === undefined) {
valueElement.classList.add("frontmatter-property-null");
valueElement.textContent = "null";
} else if (typeof value === 'object') {
valueElement.classList.add("frontmatter-property-object");
valueElement.textContent = JSON.stringify(value, null, 2);
} else if (Array.isArray(value)) {
valueElement.classList.add("frontmatter-property-array");
valueElement.textContent = JSON.stringify(value, null, 2);
} else if (typeof value === 'string' && value.startsWith('http')) {
// Make URLs clickable links
valueElement.classList.add("frontmatter-property-link");
const linkEl = document.createElement("a");
linkEl.href = value;
linkEl.textContent = value;
linkEl.target = "_blank";
valueElement.appendChild(linkEl);
} else {
valueElement.classList.add("frontmatter-property-primitive");
valueElement.textContent = String(value);
}

propertyItem.appendChild(valueElement);
propertiesContainer.appendChild(propertyItem);
}
}

protected onAfterMount(): void {
// Add CSS styles for compact frontmatter properties, closer to tags design
if (!document.getElementById('frontmatter-properties-styles')) {
const style = document.createElement('style');
style.id = 'frontmatter-properties-styles';
style.textContent = `
/* Compact Frontmatter Properties Container - similar to tags */
.frontmatter-properties-container {
padding: 0.25em 0;
margin: 0.25em 0;
border-radius: 4px;
}

/* Compact Property Items - horizontal layout like tags */
.frontmatter-property {
display: inline-flex;
align-items: center;
gap: 0.5em;
padding: 0.2em 0.4em;
margin: 0.1em 0.2em;
background-color: var(--background-secondary);
border-radius: 12px;
border: 1px solid var(--background-modifier-border);
font-size: 0.85em;
line-height: 1.3;
max-width: fit-content;
}

/* Property Key - compact like tag styling */
.frontmatter-property-key {
color: var(--text-normal);
font-weight: 500;
font-size: 0.8em;
opacity: 0.9;
background-color: var(--interactive-accent);
padding: 0.2em 0.4em;
border-radius: 8px;
font-weight: 600;
text-transform: lowercase;
}

/* Property Value - compact like tag styling */
.frontmatter-property-value {
color: var(--text-normal);
font-size: 0.85em;
line-height: 1.3;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
background-color: var(--background-primary);
padding: 0.2em 0.4em;
border-radius: 8px;
border: 1px solid var(--background-modifier-border);
min-width: 0;
flex: 1;
}

/* Link values - clickable */
.frontmatter-property-link {
color: var(--link-color);
text-decoration: none;
transition: color 0.2s ease;
}

.frontmatter-property-link:hover {
color: var(--link-color-hover);
text-decoration: underline;
}

/* Special value types - compact styling */
.frontmatter-property-null {
font-style: italic;
opacity: 0.6;
color: var(--text-muted);
}

.frontmatter-property-object,
.frontmatter-property-array {
font-family: var(--font-monospace);
font-size: 0.8em;
background-color: var(--background-primary);
border: 1px solid var(--text-muted);
padding: 0.2em 0.4em;
white-space: pre-wrap;
word-break: break-all;
border-radius: 8px;
}

/* Empty state */
.frontmatter-properties-empty {
text-align: center;
padding: 0.5em;
font-style: italic;
color: var(--text-muted);
opacity: 0.7;
font-size: 0.8em;
}

/* Responsive adjustments */
@media (max-width: 768px) {
.frontmatter-properties-container {
margin: 0.2em 0;
padding: 0.2em 0;
}

.frontmatter-property {
padding: 0.15em 0.3em;
margin: 0.05em 0.15em;
font-size: 0.8em;
gap: 0.4em;
}

.frontmatter-property-key {
font-size: 0.75em;
padding: 0.15em 0.3em;
}

.frontmatter-property-value {
font-size: 0.8em;
padding: 0.15em 0.3em;
}
}
`;
document.head.appendChild(style);
}
}

public destroy(): void {
super.destroy();

// Clean up dynamically added styles if no other frontmatter properties exist
if (document.querySelectorAll('.frontmatter-properties-container').length === 0) {
const styleEl = document.getElementById('frontmatter-properties-styles');
if (styleEl) {
styleEl.remove();
}
}
}
}
29 changes: 29 additions & 0 deletions src/frontend/main/website.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
import { GraphView } from "./graph-view";
import { Notice } from "./notifications";
import { Theme } from "./theme";
import { FrontmatterProperties } from "./frontmatter-properties";
import { LinkHandler } from "./links";
import { Shared } from "src/shared/shared";
import { FilePreviewPopover } from "./link-preview";
Expand Down Expand Up @@ -61,6 +62,7 @@ export class ObsidianWebsite {
public backlinkList: BacklinkList | undefined = undefined;
public tags: Tags | undefined = undefined;
public aliases: Aliases | undefined = undefined;
public frontmatterProperties: FrontmatterProperties | undefined = undefined;

public entryPage: string;

Expand Down Expand Up @@ -169,6 +171,12 @@ export class ObsidianWebsite {
!ObsidianSite.metadata.ignoreMetadata &&
ObsidianSite.metadata.featureOptions.alias.enabled &&
doc.documentType == DocumentType.Markdown;

const insertFrontmatterProperties =
doc.isMainDocument &&
!ObsidianSite.metadata.ignoreMetadata &&
ObsidianSite.metadata.featureOptions.frontmatterProperties.enabled &&
doc.documentType == DocumentType.Markdown;

// ------------------ BACKLINKS -----------------
if (insertBacklinks) {
Expand Down Expand Up @@ -228,6 +236,27 @@ export class ObsidianWebsite {
this.tags?.hide();
}

// ------------------ FRONTMATTER PROPERTIES -----------------
if (insertFrontmatterProperties) {
const properties = doc.info.frontmatterProperties;

if (!this.frontmatterProperties) {
this.frontmatterProperties = new FrontmatterProperties(properties ?? {});
} else {
this.frontmatterProperties?.modifyDependencies((d) => {
d.properties = properties ?? {};
});
}

if (!properties || Object.keys(properties).length === 0) {
this.frontmatterProperties?.hide();
} else {
this.frontmatterProperties?.show();
}
} else {
this.frontmatterProperties?.hide();
}

// ------------------ ALIASES -----------------
if (insertAliases) {
const aliases = doc.info.aliases;
Expand Down
Loading