mirror of
https://github.com/memodack/memodack.git
synced 2026-07-22 11:50:25 +00:00
feat: add image support to blitz mode
This commit is contained in:
parent
1495f5eef7
commit
e87fe3040a
6 changed files with 123 additions and 8 deletions
|
|
@ -60,6 +60,18 @@ export class BlitzModalService extends Modal implements IBlitzModalService {
|
|||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
|
||||
/**
|
||||
* Image
|
||||
*/
|
||||
const imageUrl = blitz?.imageUrl;
|
||||
|
||||
if (imageUrl) {
|
||||
this.createImageElement(imageUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Question
|
||||
*/
|
||||
this.createQuestionElement(blitz.question);
|
||||
|
||||
if (blitz.question !== blitz.text && blitz.text) {
|
||||
|
|
@ -71,6 +83,9 @@ export class BlitzModalService extends Modal implements IBlitzModalService {
|
|||
let nextButtonEl: HTMLButtonElement | undefined;
|
||||
let correctOptionEl: HTMLButtonElement | undefined;
|
||||
|
||||
/**
|
||||
* Answers
|
||||
*/
|
||||
const answersButtons: HTMLButtonElement[] = [];
|
||||
|
||||
const answersElement = contentEl.createEl("div");
|
||||
|
|
@ -170,4 +185,11 @@ export class BlitzModalService extends Modal implements IBlitzModalService {
|
|||
questionH2Element.setText(text);
|
||||
questionH2Element.addClass("memodack___blitz__text");
|
||||
}
|
||||
|
||||
private createImageElement(src: string) {
|
||||
const { contentEl } = this;
|
||||
const img = contentEl.createEl("img");
|
||||
img.setAttr("src", src);
|
||||
img.addClass("memodack___blitz__image");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ export interface IBlitz {
|
|||
correctAnswerId: number;
|
||||
question: string;
|
||||
answers: string[];
|
||||
text: string;
|
||||
text?: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
export interface IBlitzService {
|
||||
|
|
@ -63,6 +64,7 @@ export class BlitzService implements IBlitzService {
|
|||
question: shufflePartItem.value,
|
||||
answers: shuffleAnswers,
|
||||
text: shufflePartItem.text,
|
||||
imageUrl: shufflePartItem?.imageUrl,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { TFile } from "obsidian";
|
||||
import { inject, singleton } from "tsyringe";
|
||||
import type { IVaultService } from "./vault.service";
|
||||
import type { IWorkspaceService } from "./workspace.service";
|
||||
|
|
@ -6,11 +7,17 @@ export interface IPart {
|
|||
value: string;
|
||||
translation: string;
|
||||
text?: string;
|
||||
imageUrl?: string;
|
||||
}
|
||||
|
||||
export interface IPartsService {
|
||||
getParts(): Promise<IPart[]>;
|
||||
getSelectedParts(): IPart[];
|
||||
getSelectedParts(): Promise<IPart[]>;
|
||||
}
|
||||
|
||||
interface IExternalLink {
|
||||
title: string;
|
||||
url: string;
|
||||
}
|
||||
|
||||
@singleton()
|
||||
|
|
@ -57,23 +64,36 @@ export class PartsService implements IPartsService {
|
|||
rawText = rawText.replace(item[0], item[1]);
|
||||
});
|
||||
|
||||
parts.push({
|
||||
const data: IPart = {
|
||||
value: match[1],
|
||||
translation: match[2],
|
||||
text: rawText,
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Image
|
||||
*/
|
||||
const imageUrl = this.findAnImageUrl(content, match[1]);
|
||||
|
||||
if (imageUrl) {
|
||||
data.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
parts.push(data);
|
||||
});
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
getSelectedParts(): IPart[] {
|
||||
async getSelectedParts(): Promise<IPart[]> {
|
||||
const activeFile = this.workspaceService.getActiveFile();
|
||||
|
||||
if (!activeFile) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const content = await this.vaultService.read(activeFile);
|
||||
|
||||
const parts: IPart[] = [];
|
||||
|
||||
const selection = window.getSelection();
|
||||
|
|
@ -99,7 +119,18 @@ export class PartsService implements IPartsService {
|
|||
|
||||
// Format the string and add it to the array
|
||||
if (value && translation) {
|
||||
parts.push({ value, translation });
|
||||
const data: IPart = { value, translation };
|
||||
|
||||
/**
|
||||
* Image
|
||||
*/
|
||||
const imageUrl = this.findAnImageUrl(content, value);
|
||||
|
||||
if (imageUrl) {
|
||||
data.imageUrl = imageUrl;
|
||||
}
|
||||
|
||||
parts.push(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -112,4 +143,41 @@ export class PartsService implements IPartsService {
|
|||
|
||||
return [];
|
||||
}
|
||||
|
||||
private findAnImageUrl(content: string, search: string): string | null {
|
||||
const filteredImages = this.extractExternalLinks(content).map((item) => ({
|
||||
title: item.title,
|
||||
url: this.getResourcePath(item.url),
|
||||
}));
|
||||
|
||||
const image = filteredImages.find((item) => item.title === search);
|
||||
|
||||
if (!image) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return image.url;
|
||||
}
|
||||
|
||||
private extractExternalLinks(content: string): IExternalLink[] {
|
||||
// [text](url)
|
||||
const regex = /\[([^\]]+)\]\(([^)]+)\)/g;
|
||||
|
||||
return [...content.matchAll(regex)].map((m) => ({
|
||||
title: m[1],
|
||||
url: m[2],
|
||||
}));
|
||||
}
|
||||
|
||||
private getResourcePath(path: string) {
|
||||
let url = path;
|
||||
|
||||
const file = this.vaultService.getAbstractFileByPath(url);
|
||||
|
||||
if (file instanceof TFile) {
|
||||
url = this.vaultService.getResourcePath(file);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export class RibbonIconService implements IRibbonIconService {
|
|||
|
||||
let parts: IPart[] = [];
|
||||
|
||||
parts = this.partsService.getSelectedParts();
|
||||
parts = await this.partsService.getSelectedParts();
|
||||
|
||||
if (!parts.length) {
|
||||
parts = await this.partsService.getParts();
|
||||
|
|
|
|||
|
|
@ -1,10 +1,12 @@
|
|||
import type { TFile, TFolder, Vault } from "obsidian";
|
||||
import type { TAbstractFile, TFile, TFolder, Vault } from "obsidian";
|
||||
import { inject, singleton } from "tsyringe";
|
||||
|
||||
export interface IVaultService {
|
||||
read(file: TFile): Promise<string>;
|
||||
getConfigDir(): string;
|
||||
createFolder(path: string): Promise<TFolder>;
|
||||
getAbstractFileByPath(path: string): TAbstractFile | null;
|
||||
getResourcePath(file: TFile): string;
|
||||
}
|
||||
|
||||
@singleton()
|
||||
|
|
@ -22,4 +24,12 @@ export class VaultService implements IVaultService {
|
|||
createFolder(path: string): Promise<TFolder> {
|
||||
return this.vault.createFolder(path);
|
||||
}
|
||||
|
||||
getAbstractFileByPath(path: string): TAbstractFile | null {
|
||||
return this.vault.getAbstractFileByPath(path);
|
||||
}
|
||||
|
||||
getResourcePath(file: TFile): string {
|
||||
return this.vault.getResourcePath(file);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,6 +86,19 @@
|
|||
margin-top: 0;
|
||||
}
|
||||
|
||||
.memodack___blitz__image {
|
||||
height: 170px;
|
||||
min-height: 170px;
|
||||
border-radius: var(--modal-radius);
|
||||
border: var(--modal-border-width) solid var(--modal-border-color);
|
||||
box-shadow: var(--input-shadow);
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
object-fit: cover;
|
||||
aspect-ratio: 4 / 3;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.memodack___syntax {
|
||||
text-decoration-color: #6f6f6f;
|
||||
|
|
|
|||
Loading…
Reference in a new issue