Compare commits

...

10 commits

Author SHA1 Message Date
mara.froehlich
53a4f03995 release version 1.1.0 2025-06-25 20:40:55 +02:00
mara.froehlich
253668b983 allow user to position image within container 2025-06-25 20:39:06 +02:00
mara.froehlich
0777249a18 release version 1.0.7 2025-01-27 19:13:06 +01:00
mara.froehlich
6f1d8be50b correctly format avatar view in reading mode 2025-01-27 19:04:29 +01:00
mara.froehlich
06e96c659e fix look of description 2025-01-27 18:56:56 +01:00
mara.froehlich
123a7a2907 fix overlapping text in certain situations 2025-01-27 18:48:09 +01:00
mara.froehlich
2daa4ef222 fix issue with ts build 2025-01-19 23:46:08 +01:00
mara.froehlich
9c74ec76d9 release version 1.0.6 2025-01-19 23:33:56 +01:00
mara.froehlich
ce254f6ab0 allow user to adjust size 2025-01-19 23:18:47 +01:00
mara.froehlich
20f7b30d75 allow user to configure side on which the avatar will be displayed. 2025-01-19 23:00:36 +01:00
8 changed files with 141 additions and 46 deletions

View file

@ -22,12 +22,27 @@ description: This will be displayed as the description!
```
````
**In live preview mode**, click on the image to change it *(then either select one from your vault, or paste in a URL to a picture online)*, or edit the description by clicking on it *(Don't forget to click the button to save your changes!)*
**In live preview mode**, click on the image to change it _(then either select one from your vault, or paste in a URL to a picture online)_, or edit the description by clicking on it _(Don't forget to click the button to save your changes!)_
![Edit Mode](./docs/avatar_edit.PNG)
The description editor supports all markdown features of Obsidian.
## More options
The following options are available through the YAML codeblock:
- `image?: string`
- A path or URL to an image. Optional, defaults to a picture of the file path initials.
- `description: string`
- The description displayed next to the image.
- `side?: "left" | "right"`
- The side on which the avatar will be displayed. Optional, defaults to `"left"`.
- `size?: "small" | "medium" | "large" | number`
- The size of the avatar. May be either `small` (180x180px), `medium` (240x240px), `large` (320x320px), or any custom width as a number. Optional, defaults to `medium`.
- `objectPosition?: string`
- How to position the image within the square container surrounding it. For example, `"left"` would show the left side of a landscape picture, `"right"` the right side. <small>Refer to https://developer.mozilla.org/en-US/docs/Web/CSS/object-position for more information.</small>
## Installation
### Using Obsidian
@ -36,7 +51,7 @@ The description editor supports all markdown features of Obsidian.
2. Open `Settings` -> `Community Plugins`
3. Click on `Turn on Community plugins` if prompted
4. Click on `Browse`, and search for `Avatar`
6. Install & enable the plugin
5. Install & enable the plugin
### Manual installation

View file

@ -1,10 +1,10 @@
{
"id": "avatar",
"name": "Avatar",
"version": "1.0.5",
"version": "1.1.0",
"minAppVersion": "0.15.0",
"description": "Display an avatar image in your notes.",
"author": "froehlichA",
"authorUrl": "https://github.com/froehlichA",
"isDesktopOnly": false
}
}

4
package-lock.json generated
View file

@ -1,12 +1,12 @@
{
"name": "obsidian-avatar",
"version": "1.0.5",
"version": "1.0.7",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "obsidian-avatar",
"version": "1.0.5",
"version": "1.0.7",
"license": "MIT",
"devDependencies": {
"@tsconfig/svelte": "^3.0.0",

View file

@ -1,6 +1,6 @@
{
"name": "obsidian-avatar",
"version": "1.0.5",
"version": "1.1.0",
"description": "An obsidian plugin for displaying an avatar image in front of your notes. ",
"main": "main.js",
"scripts": {

View file

@ -2,18 +2,45 @@
import {
App,
MarkdownPostProcessorContext,
MarkdownRenderer, MarkdownView,
TFile
MarkdownRenderer,
MarkdownView,
TFile,
} from "obsidian";
import Fab from "./Fab.svelte";
import ObsidianIcon from "./ObsidianIcon.svelte";
import type {SetState, State} from "../core/stateProviders";
import {SelectImageModal} from "./SelectImageModal";
import {AvatarPlugin} from "../plugin";
import {onMount} from "svelte";
import type { SetState, State } from "../core/stateProviders";
import { SelectImageModal } from "./SelectImageModal";
import { AvatarPlugin } from "../plugin";
import { onMount } from "svelte";
interface AvatarViewState {
image: string;
image?: string;
/**
* The side on which the image will be displayed.
*
* @default "left"
*/
side?: "left" | "right";
/**
* The size of the avatar image.
*
* small: 180x180
* medium: 240x240
* large: 300x300
*
* @default "medium"
*/
size?: "small" | "medium" | "large" | number;
/**
* Alignment of the avatar image within the container.
*
* Corresponds to the `object-position` CSS property.
*
* @see https://developer.mozilla.org/en-US/docs/Web/CSS/object-position
*
* @default "50% 50%"
*/
objectPosition?: string;
description: string;
}
@ -31,6 +58,16 @@
let inSourceMode = false;
$: fallbackImage = `https://ui-avatars.com/api/?name=${ctx?.sourcePath.split("/").at(-1) ?? "::"}&size=240`;
$: avatarSize = state.size
? typeof state.size === "string"
? {
small: 180,
medium: 240,
large: 300,
}[state.size]
: state.size
: 240;
$: objectPosition = state.objectPosition;
onMount(() => {
inSourceMode = isSourceMode();
@ -46,7 +83,7 @@
}
function enterEditMode() {
if(inSourceMode && !editMode) {
if (inSourceMode && !editMode) {
editMode = true;
queueMicrotask(() => {
descriptionEditEl.focus();
@ -64,7 +101,7 @@
}
function updateImage() {
if(inSourceMode) {
if (inSourceMode) {
new SelectImageModal(app, (path) => {
setState((fm) => {
fm.image = path;
@ -75,33 +112,49 @@
function normalizeImgPath(src: string): string {
const file = app.vault.getAbstractFileByPath(src);
if(file && file instanceof TFile) {
if (file && file instanceof TFile) {
return app.vault.getResourcePath(file);
}
return src;
}
$: if(descriptionPreviewEl && plugin && state && state?.description) {
descriptionPreviewEl.innerHTML = '';
MarkdownRenderer.renderMarkdown(state.description, descriptionPreviewEl, ctx?.sourcePath ?? "", plugin);
$: if (descriptionPreviewEl && plugin && state && state?.description) {
descriptionPreviewEl.innerHTML = "";
MarkdownRenderer.renderMarkdown(
state.description,
descriptionPreviewEl,
ctx?.sourcePath ?? "",
plugin,
);
}
</script>
<div class="flex">
<div class="avatar-view flex" class:reverse={state?.side === "right"}>
<div
class="avatar relative"
class="avatar-container relative"
on:click={updateImage}
on:mouseenter={() => hoverOnImage = true}
on:mouseleave={() => hoverOnImage = false}
on:mouseenter={() => (hoverOnImage = true)}
on:mouseleave={() => (hoverOnImage = false)}
>
<img class="avatar" alt="Avatar" src={normalizeImgPath(state?.image) ?? fallbackImage} />
<img
class="avatar"
alt="Avatar"
style:width={`${avatarSize}px`}
style:height={`${avatarSize}px`}
style:object-position={objectPosition}
src={state.image ? normalizeImgPath(state?.image) : fallbackImage}
/>
{#if inSourceMode && hoverOnImage}
<Fab>
<ObsidianIcon id="edit"></ObsidianIcon>
</Fab>
{/if}
</div>
<div class="description" on:click={enterEditMode}>
<div
class="description relative"
on:click={enterEditMode}
class:editmode={!!editMode}
>
<textarea
class="textarea"
bind:this={descriptionEditEl}
@ -110,7 +163,7 @@
bind:value={state.description}
></textarea>
<span
class="avatar-plugin--md-preview with-placeholder"
class="avatar-plugin--md with-placeholder"
hidden={editMode}
bind:this={descriptionPreviewEl}
data-placeholder="Write your story..."
@ -131,20 +184,25 @@
justify-content: center;
}
.flex.reverse {
flex-direction: row-reverse;
}
@media (min-width: 992px) {
.flex {
flex-wrap: nowrap;
}
}
.relative {
position: relative;
}
.avatar {
.avatar-container {
flex: 0 0 auto;
width: 240px;
height: 240px;
}
.avatar {
object-fit: cover;
border-radius: 6px;
}
@ -152,10 +210,13 @@
.description {
flex: 1 1 auto;
word-break: break-word;
padding: 6px;
border-radius: 6px;
}
.description.editmode {
padding: 6px;
}
.textarea {
width: 100%;
height: 100%;

View file

@ -1,13 +1,20 @@
import type {MarkdownPostProcessorContext} from "obsidian";
import type {CodeBlockProcessorProps} from "./renderCodeBlockProcessor";
import {parseYaml, stringifyYaml} from "obsidian";
import type { App, MarkdownPostProcessorContext } from "obsidian";
import type { CodeBlockProcessorProps } from "./renderCodeBlockProcessor";
import { parseYaml, stringifyYaml } from "obsidian";
export type State<T> = Partial<T>;
export type SetState<T> = (setter: (state: Partial<T>) => void) => void;
export type StateProvider<T> = (props: CodeBlockProcessorProps, source: string, node: HTMLElement, ctx: MarkdownPostProcessorContext) => {
state: State<T>,
setState: SetState<T>
declare let app: App;
export type StateProvider<T> = (
props: CodeBlockProcessorProps,
source: string,
node: HTMLElement,
ctx: MarkdownPostProcessorContext
) => {
state: State<T>;
setState: SetState<T>;
};
export function withCodeblockState<T>(): StateProvider<T> {
@ -18,12 +25,12 @@ export function withCodeblockState<T>(): StateProvider<T> {
} catch (_) {}
const setState: SetState<T> = (stateSetter) => {
let newState = { ...state };
const newState = { ...state };
stateSetter(newState);
const newStateStr: string = stringifyYaml(newState);
const info = ctx.getSectionInfo(node);
if(info) {
if (info) {
app.workspace.activeEditor?.editor?.replaceRange(
newStateStr + "```",
{ line: info.lineStart + 1, ch: 0 },
@ -34,7 +41,7 @@ export function withCodeblockState<T>(): StateProvider<T> {
return {
state,
setState
}
setState,
};
};
}

View file

@ -2,6 +2,15 @@
float: right;
}
.avatar-plugin--md-preview > * {
margin-top: 0;
.markdown-preview-view .avatar-view {
margin-block-start: var(--p-spacing);
margin-block-end: var(--p-spacing);
}
.avatar-plugin--md > *:first-child {
margin-block-start: 0;
}
.avatar-plugin--md > *:last-child {
margin-block-end: 0;
}

View file

@ -4,5 +4,8 @@
"1.0.2": "0.15.0",
"1.0.3": "0.15.0",
"1.0.4": "0.15.0",
"1.0.5": "0.15.0"
}
"1.0.5": "0.15.0",
"1.0.6": "0.15.0",
"1.0.7": "0.15.0",
"1.1.0": "0.15.0"
}