Skip to content

Commit 1ecb0c5

Browse files
authored
Merge pull request #6 from jparkerweb/tweaks
add y-position option
2 parents 237cf93 + 4b87579 commit 1ecb0c5

File tree

3 files changed

+121
-57
lines changed

3 files changed

+121
-57
lines changed

README.md

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ Pexels Banner is a plugin for Obsidian that allows you to automatically add beau
66

77
- Automatically fetch and display banner images from Pexels based on keywords
88
- Use direct URLs for custom images
9+
- Use local images from your vault
10+
- Use Obsidian internal links to images
911
- Customize image size and orientation
1012
- Set default keywords for when no specific keyword is provided
13+
- Adjust vertical position of the banner image globally or per note
1114
- Seamless integration with Obsidian's interface
1215

1316
## Installation
@@ -21,19 +24,38 @@ Pexels Banner is a plugin for Obsidian that allows you to automatically add beau
2124

2225
1. Obtain a free API key from [Pexels](https://www.pexels.com/api/)
2326
2. In Obsidian, go to Settings > Pexels Banner and enter your API key
24-
3. In any note, add a `pexels-banner` field to the frontmatter with keywords for the desired image or a direct URL:
25-
```yaml
26-
---
27-
pexels-banner: blue turtle
28-
---
27+
3. In any note, add a `pexels-banner` field to the frontmatter with keywords for the desired image, a direct URL, a path to a local image, or an Obsidian internal link:
2928

30-
# Or use a direct URL:
29+
```yaml
30+
---
31+
pexels-banner: blue turtle
32+
---
3133

32-
---
33-
pexels-banner: https://example.com/image.jpg
34-
---
35-
```
36-
4. The plugin will automatically fetch and display a relevant banner image at the top of your note
34+
# Or use a direct URL:
35+
36+
---
37+
pexels-banner: https://example.com/image.jpg
38+
---
39+
40+
# Or use a local image:
41+
42+
---
43+
pexels-banner: /path/to/local/image.jpg
44+
---
45+
46+
# Or use an Obsidian internal link:
47+
48+
---
49+
pexels-banner: [[path/to/internal/image.jpg]]
50+
---
51+
52+
# Specify a custom y-position for the image (0-100):
53+
54+
---
55+
pexels-banner: nature
56+
pexels-banner-y-position: 30
57+
---
58+
```
3759

3860
## Configuration
3961

@@ -43,6 +65,9 @@ In the plugin settings, you can customize:
4365
- Image orientation (landscape, portrait, square)
4466
- Number of images to fetch (1-50)
4567
- Default keywords for when no specific keyword is provided
68+
- Global y-position of the banner image (0-100)
69+
70+
The global y-position can be overridden on a per-note basis using the `pexels-banner-y-position` frontmatter field.
4671

4772
## Example Note Screenshot
4873

src/main.js

Lines changed: 79 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ const DEFAULT_SETTINGS = {
55
imageSize: 'medium',
66
imageOrientation: 'landscape',
77
numberOfImages: 10,
8-
defaultKeywords: 'nature,abstract,landscape,technology,art,cityscape,wildlife,ocean,mountains,forest,space,architecture,food,travel,science,music,sports,fashion,business,education,health,culture,history,weather,transportation,industry,people,animals,plants,patterns'
8+
defaultKeywords: 'nature,abstract,landscape,technology,art,cityscape,wildlife,ocean,mountains,forest,space,architecture,food,travel,science,music,sports,fashion,business,education,health,culture,history,weather,transportation,industry,people,animals,plants,patterns',
9+
yPosition: 50 // Add this line: default to 50% (center)
910
};
1011

1112
module.exports = class PexelsBannerPlugin extends Plugin {
@@ -17,6 +18,7 @@ module.exports = class PexelsBannerPlugin extends Plugin {
1718
lastRequestTime: 0,
1819
minInterval: 1000 // 1 second between requests
1920
};
21+
lastYPositions = new Map();
2022

2123
async onload() {
2224
await this.loadSettings();
@@ -73,9 +75,11 @@ module.exports = class PexelsBannerPlugin extends Plugin {
7375
if (activeLeaf && activeLeaf.view.file === file) {
7476
const frontmatter = this.app.metadataCache.getFileCache(file)?.frontmatter;
7577
const newKeyword = frontmatter && frontmatter['pexels-banner'];
78+
const newYPosition = frontmatter && (frontmatter['pexels-banner-y-position'] || frontmatter['pexels-banner-y']);
7679
const oldKeyword = this.lastKeywords.get(file.path);
80+
const oldYPosition = this.lastYPositions.get(file.path);
7781

78-
if (newKeyword !== oldKeyword) {
82+
if (newKeyword !== oldKeyword || newYPosition !== oldYPosition) {
7983
await this.updateBanner(activeLeaf.view, true);
8084
}
8185
}
@@ -91,17 +95,29 @@ module.exports = class PexelsBannerPlugin extends Plugin {
9195
async updateBanner(view, isContentChange) {
9296
const frontmatter = this.app.metadataCache.getFileCache(view.file)?.frontmatter;
9397
const contentEl = view.contentEl;
94-
await this.addPexelsBanner(contentEl, { frontmatter, file: view.file, isContentChange });
98+
const customYPosition = frontmatter && (frontmatter['pexels-banner-y-position'] || frontmatter['pexels-banner-y']);
99+
const yPosition = customYPosition !== undefined ? customYPosition : this.settings.yPosition;
100+
101+
await this.addPexelsBanner(contentEl, {
102+
frontmatter,
103+
file: view.file,
104+
isContentChange,
105+
yPosition
106+
});
107+
108+
// Update the lastYPositions Map
109+
this.lastYPositions.set(view.file.path, yPosition);
95110
}
96111

97112
async addPexelsBanner(el, ctx) {
98-
const { frontmatter, file, isContentChange } = ctx;
113+
const { frontmatter, file, isContentChange, yPosition } = ctx;
99114
if (frontmatter && frontmatter['pexels-banner']) {
100115
let input = frontmatter['pexels-banner'];
101116

102-
// If input is an array, try to get the first string element
117+
// Handle the case where input is an array of arrays
103118
if (Array.isArray(input)) {
104-
input = input.flat().find(item => typeof item === 'string') || '';
119+
// Reconstruct the Obsidian link format
120+
input = `[[${input.flat(Infinity).join('')}]]`;
105121
}
106122

107123
const inputType = this.getInputType(input);
@@ -129,34 +145,33 @@ module.exports = class PexelsBannerPlugin extends Plugin {
129145
}
130146
}
131147

132-
if (imageUrl) {
133-
// Find the appropriate parent elements
134-
const previewView = el.querySelector('.markdown-preview-view');
135-
const sourceView = el.querySelector('.markdown-source-view');
148+
// Find the appropriate parent elements
149+
const previewView = el.querySelector('.markdown-preview-view');
150+
const sourceView = el.querySelector('.markdown-source-view');
136151

137-
// Remove existing banners if present
138-
const existingBanners = el.querySelectorAll('.pexels-banner-image');
139-
existingBanners.forEach(banner => banner.remove());
152+
// Remove existing banners if present
153+
const existingBanners = el.querySelectorAll('.pexels-banner-image');
154+
existingBanners.forEach(banner => banner.remove());
140155

141-
// Create the banner div
142-
const bannerDiv = createDiv({ cls: 'pexels-banner-image' });
143-
bannerDiv.style.backgroundImage = `url('${imageUrl}')`;
156+
// Create the banner div
157+
const bannerDiv = createDiv({ cls: 'pexels-banner-image' });
158+
bannerDiv.style.backgroundImage = `url('${imageUrl}')`;
159+
bannerDiv.style.backgroundPosition = `center ${yPosition}%`;
144160

145-
// Insert the banner div in the appropriate locations
146-
if (previewView) {
147-
previewView.prepend(bannerDiv.cloneNode(true));
148-
}
149-
if (sourceView) {
150-
const cmSizer = sourceView.querySelector('.cm-sizer');
151-
if (cmSizer) {
152-
cmSizer.prepend(bannerDiv.cloneNode(true));
153-
} else {
154-
sourceView.prepend(bannerDiv.cloneNode(true));
155-
}
161+
// Insert the banner div in the appropriate locations
162+
if (previewView) {
163+
previewView.prepend(bannerDiv.cloneNode(true));
164+
}
165+
if (sourceView) {
166+
const cmSizer = sourceView.querySelector('.cm-sizer');
167+
if (cmSizer) {
168+
cmSizer.prepend(bannerDiv.cloneNode(true));
169+
} else {
170+
sourceView.prepend(bannerDiv.cloneNode(true));
156171
}
157-
158-
el.classList.add('pexels-banner');
159172
}
173+
174+
el.classList.add('pexels-banner');
160175
} else {
161176
// Remove the banners if 'pexels-banner' is not in frontmatter
162177
const existingBanners = el.querySelectorAll('.pexels-banner-image');
@@ -239,21 +254,15 @@ module.exports = class PexelsBannerPlugin extends Plugin {
239254
}
240255

241256
getInputType(input) {
242-
// If input is an array, try to get the first string element
243-
if (Array.isArray(input)) {
244-
input = input.flat().find(item => typeof item === 'string') || '';
245-
}
246-
247-
// Check if input is a string
248257
if (typeof input !== 'string') {
249258
return 'invalid';
250259
}
251260

252-
// Trim the input
253-
input = input.trim();
261+
// Trim the input and remove surrounding quotes if present
262+
input = input.trim().replace(/^["'](.*)["']$/, '$1');
254263

255264
// Check if it's an Obsidian internal link
256-
if (input.match(/^\[\[.*\]\]$/)) {
265+
if (input.includes('[[') && input.includes(']]')) {
257266
return 'obsidianLink';
258267
}
259268

@@ -274,8 +283,10 @@ module.exports = class PexelsBannerPlugin extends Plugin {
274283
}
275284

276285
getPathFromObsidianLink(link) {
277-
// Remove the [[ and ]] from the link
278-
const innerLink = link.slice(2, -2);
286+
// Remove the [[ from the beginning of the link
287+
let innerLink = link.startsWith('[[') ? link.slice(2) : link;
288+
// Remove the ]] from the end if it exists
289+
innerLink = innerLink.endsWith(']]') ? innerLink.slice(0, -2) : innerLink;
279290
// Split by '|' in case there's an alias, and take the first part
280291
const path = innerLink.split('|')[0];
281292
// Resolve the path within the vault
@@ -290,11 +301,20 @@ module.exports = class PexelsBannerPlugin extends Plugin {
290301
const blob = new Blob([arrayBuffer], { type: `image/${file.extension}` });
291302
return URL.createObjectURL(blob);
292303
} catch (error) {
304+
console.error('Error reading vault image:', error);
293305
return null;
294306
}
295307
}
296308
return null;
297309
}
310+
311+
updateAllBanners() {
312+
this.app.workspace.iterateAllLeaves(leaf => {
313+
if (leaf.view.getViewType() === "markdown") {
314+
this.updateBanner(leaf.view, false);
315+
}
316+
});
317+
}
298318
}
299319

300320
class PexelsBannerSettingTab extends PluginSettingTab {
@@ -398,32 +418,50 @@ class PexelsBannerSettingTab extends PluginSettingTab {
398418
textarea.style.height = '100px';
399419
});
400420

421+
new Setting(mainContent)
422+
.setName('Image Vertical Position')
423+
.setDesc('Set the vertical position of the image (0-100)')
424+
.addSlider(slider => slider
425+
.setLimits(0, 100, 1)
426+
.setValue(this.plugin.settings.yPosition)
427+
.setDynamicTooltip()
428+
.onChange(async (value) => {
429+
this.plugin.settings.yPosition = value;
430+
await this.plugin.saveSettings();
431+
this.plugin.updateAllBanners();
432+
})
433+
);
434+
401435
// How to use section
402436
new Setting(mainContent)
403437
.setName('How to use')
404438
.setHeading();
405439

406440
const instructionsEl = mainContent.createEl('div', {cls: 'pexels-banner-section'});
407-
instructionsEl.createEl('p', {text: 'Add a "pexels-banner" field to your note\'s frontmatter with keywords for the image you want, or a direct URL to an image.'});
441+
instructionsEl.createEl('p', {text: 'Add a "pexels-banner" field to your note\'s frontmatter with keywords for the image you want, or a direct URL to an image. You can also specify a custom y-position for the image.'});
408442
const codeEl = instructionsEl.createEl('pre');
409443
codeEl.createEl('code', {text:
410444
`---
411445
pexels-banner: blue turtle
446+
pexels-banner-y: 30
412447
---
413448
414449
# Or use a direct URL:
415450
---
416451
pexels-banner: https://example.com/image.jpg
452+
pexels-banner-y: 70
417453
---
418454
419455
# Or use a path to an image in the vault:
420456
---
421457
pexels-banner: Assets/my-image.png
458+
pexels-banner-y: 0
422459
---
423460
424461
# Or use an Obsidian internal link:
425462
---
426463
pexels-banner: [[example-image.png]]
464+
pexels-banner-y: 100
427465
---`
428466
});
429467

styles.css

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,17 +29,18 @@
2929
/* ------------------------------------------------------------------ */
3030
/* -- push frontmatter container down to give room to banner image -- */
3131
/* ------------------------------------------------------------------ */
32-
.pexels-banner .metadata-container {
33-
margin-top: 210px;
34-
}
3532
.pexels-banner .frontmatter-container,
3633
.pexels-banner .metadata-container {
3734
position: relative;
3835
z-index: 2;
39-
margin-top: 150px !important;
36+
}
37+
.pexels-banner .markdown-preview-section {
38+
margin-top: 150px !important;
4039
}
4140

42-
/* Settings Page Styles */
41+
/* -------------------------- */
42+
/* -- Settings Page Styles -- */
43+
/* -------------------------- */
4344
.pexels-banner-settings {
4445
padding: 20px;
4546
max-width: 800px;

0 commit comments

Comments
 (0)