diff --git a/client/src/components/AudioPreview.vue b/client/src/components/AudioPreview.vue new file mode 100644 index 0000000..29a7ca1 --- /dev/null +++ b/client/src/components/AudioPreview.vue @@ -0,0 +1,208 @@ + + + + diff --git a/client/src/components/MarkDown.vue b/client/src/components/MarkDown.vue index a740313..79226f0 100644 --- a/client/src/components/MarkDown.vue +++ b/client/src/components/MarkDown.vue @@ -7,8 +7,8 @@ min-height: 520px; svg { - max-width: 100% !important; - height: auto !important; + max-width: 100%; + height: auto; g > * :not(text) { stroke: #ccc !important; @@ -16,8 +16,13 @@ } img { - max-width: 100% !important; - height: auto !important; + max-width: 100%; + height: auto; + } + + audio { + max-width: 100%; + margin: 10px 0; } } @@ -34,7 +39,7 @@ const props = defineProps({ markdown: { type: String as PropType, }, - customImageUrls: { + customFileUrls: { type: Object as PropType<{ [sha256: string]: File }>, default: () => {}, }, @@ -47,11 +52,11 @@ watch(props, async () => { emits("loading", true); sanitizedHtml.value = await MarkdownConverterClient.Instance.convert(props.markdown ?? "", async (token: string) => { if (!token || token.length < 10) { - return Promise.reject("Image hash too short"); + return Promise.reject("File hash too short"); } - const urls = Object.keys(props.customImageUrls).filter((it) => it.startsWith(token)); + const urls = Object.keys(props.customFileUrls).filter((it) => it.startsWith(token)); if (urls && urls.length === 1) { - const customFile = props.customImageUrls[urls[0]]; + const customFile = props.customFileUrls[urls[0]]; return blobToArray(customFile).then((it) => bytesToDataUrl(customFile.type, it)); } return Promise.reject("No custom file found"); diff --git a/client/src/i18n/de.json b/client/src/i18n/de.json index f5fbfb2..bea1511 100644 --- a/client/src/i18n/de.json +++ b/client/src/i18n/de.json @@ -24,9 +24,11 @@ "cancel": "Abbrechen", "edit": "Bearbeiten", "insert_image": "Bild einfügen", + "insert_audio": "Audio einfügen", "delete": "Löschen", "delete_image": "Bild löschen", - "remove": "Bildrefernz aus Text entfernen", + "delete_audio": "Audio löschen", + "remove": "Dateireferenz aus Text entfernen", "read": "Lesen", "create_post": "Post erstellen", "roles": "Rollen", @@ -62,8 +64,8 @@ "tags": "Schlagworte", "enter": "Geben Sie Schlagworte ein..." }, - "imageupload": "keine angehängten Bilder | ein angehängtes Bild | {count} angehängte Bilder", - "imageupload_hint": "(Bild per Drag & Drop in den Text ziehen)" + "fileupload": "keine angehängten Dateien | eine angehängte Datei | {count} angehängte Dateien", + "fileupload_hint": "(Datei per Drag & Drop in den Text ziehen)" }, "confirm": { "title": "Löschen bestätigen", diff --git a/client/src/i18n/en.json b/client/src/i18n/en.json index 61729f5..15d3bed 100644 --- a/client/src/i18n/en.json +++ b/client/src/i18n/en.json @@ -24,8 +24,10 @@ "cancel": "Cancel", "edit": "Edit", "insert_image": "Insert image", + "insert_audio": "Insert audio", "delete_image": "Delete image", - "remove": "Remove image reference from text", + "delete_audio": "Delete audio", + "remove": "Remove file reference from text", "read": "Read", "create_post": "Create Post", "roles": "Roles", @@ -57,8 +59,8 @@ "preview": { "title": "Preview" }, - "imageupload": "Upload image", - "imageupload_hint": "(drag onto textarea to insert)", + "fileupload": "Upload files", + "fileupload_hint": "(drag onto textarea to insert)", "tags": "Tags" }, "confirm": { diff --git a/client/src/util/api-client.ts b/client/src/util/api-client.ts index a5d954f..306d8d8 100644 --- a/client/src/util/api-client.ts +++ b/client/src/util/api-client.ts @@ -8,7 +8,7 @@ import type { LoggedInUserInfo, NewPostRequestDto, PublicPost, - SupportedImageMimeType, + SupportedFileMimeType, } from "@fumix/fu-blog-common"; import { HttpHeader, imageBytesToDataUrl } from "@fumix/fu-blog-common"; @@ -16,8 +16,8 @@ export type ApiUrl = `/api/${string}`; async function callServer< RequestType, - ResponseMimeType extends SupportedImageMimeType | JsonMimeType, - ResponseType = ResponseMimeType extends JsonMimeType ? any : ResponseMimeType extends SupportedImageMimeType ? ArrayBuffer : any, + ResponseMimeType extends SupportedFileMimeType | JsonMimeType, + ResponseType = ResponseMimeType extends JsonMimeType ? any : ResponseMimeType extends SupportedFileMimeType ? ArrayBuffer : any, >( url: ApiUrl, method: "GET" | "POST", @@ -101,7 +101,7 @@ export class OpenAiEndpoints { return callServer("/api/utility/chatGptSummarize", "POST", "application/json", { json: text }); } static async dallEGenerateImage(prompt: string): Promise { - return callServer("/api/utility/dallEGenerateImage", "POST", "image/png", { + return callServer("/api/utility/dallEGenerateImage", "POST", "image/png", { json: prompt, }) .then((it) => { diff --git a/client/src/util/storage.ts b/client/src/util/storage.ts index b66764c..d80a3f3 100644 --- a/client/src/util/storage.ts +++ b/client/src/util/storage.ts @@ -80,8 +80,6 @@ export function loadCssPreference(): UserTheme { ); } - - // // General purpose helper functions, should only be called indirectly // diff --git a/client/src/views/PostFormView.vue b/client/src/views/PostFormView.vue index 26a64b8..ed94a01 100644 --- a/client/src/views/PostFormView.vue +++ b/client/src/views/PostFormView.vue @@ -1,139 +1,156 @@