Compare commits

...

4 Commits

Author SHA1 Message Date
Philip Okugbe b4f009513e fix: resize handle clipping (#1990) 2026-03-04 12:24:46 +00:00
Philipinho fcffa3dfa0 fix media 2026-03-04 12:08:08 +00:00
Philipinho 1980b94825 0.70.1 2026-03-04 11:57:31 +00:00
Philip Okugbe bea1637519 fix: image fallback regression (#1989)
* fix: image fallback regression

* fix image preview on upload

* fix image loading
2026-03-04 11:51:43 +00:00
10 changed files with 58 additions and 23 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "client",
"private": true,
"version": "0.70.0",
"version": "0.70.1",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
@@ -2,6 +2,7 @@
display: flex;
justify-content: center;
align-items: center;
max-width: 100%;
border-radius: 8px;
overflow: hidden;
animation: pulse 1.2s ease-in-out infinite;
@@ -2,6 +2,7 @@
display: flex;
justify-content: center;
align-items: center;
max-width: 100%;
border-radius: 8px;
overflow: hidden;
animation: pulse 1.2s ease-in-out infinite;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "server",
"version": "0.70.0",
"version": "0.70.1",
"description": "",
"author": "",
"private": true,
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "docmost",
"homepage": "https://docmost.com",
"version": "0.70.0",
"version": "0.70.1",
"private": true,
"scripts": {
"build": "nx run-many -t build",
+8 -5
View File
@@ -1,6 +1,7 @@
import { Node, mergeAttributes, ResizableNodeView } from "@tiptap/core";
import type { ResizableNodeViewDirection } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { normalizeFileUrl } from "./media-utils";
export type DrawioResizeOptions = {
enabled: boolean;
@@ -223,7 +224,7 @@ export const Drawio = Node.create<DrawioOptions>({
}
const el = document.createElement("img");
el.src = node.attrs.src;
el.src = normalizeFileUrl(node.attrs.src);
el.alt = node.attrs.title || "";
el.style.display = "block";
el.style.maxWidth = "100%";
@@ -259,7 +260,7 @@ export const Drawio = Node.create<DrawioOptions>({
}
if (updatedNode.attrs.src !== currentNode.attrs.src) {
el.src = updatedNode.attrs.src || "";
el.src = normalizeFileUrl(updatedNode.attrs.src);
}
const w = updatedNode.attrs.width;
@@ -317,12 +318,14 @@ export const Drawio = Node.create<DrawioOptions>({
});
}
// Hide until image loads
dom.style.visibility = "hidden";
// Show skeleton background while image loads from server
dom.style.pointerEvents = "none";
dom.style.background =
"light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))";
el.onload = () => {
dom.style.visibility = "";
dom.style.pointerEvents = "";
dom.style.background = "";
};
return nodeView;
+8 -5
View File
@@ -1,6 +1,7 @@
import { Node, mergeAttributes, ResizableNodeView } from "@tiptap/core";
import type { ResizableNodeViewDirection } from "@tiptap/core";
import { ReactNodeViewRenderer } from "@tiptap/react";
import { normalizeFileUrl } from "./media-utils";
export type ExcalidrawResizeOptions = {
enabled: boolean;
@@ -223,7 +224,7 @@ export const Excalidraw = Node.create<ExcalidrawOptions>({
}
const el = document.createElement("img");
el.src = node.attrs.src;
el.src = normalizeFileUrl(node.attrs.src);
el.alt = node.attrs.title || "";
el.style.display = "block";
el.style.maxWidth = "100%";
@@ -259,7 +260,7 @@ export const Excalidraw = Node.create<ExcalidrawOptions>({
}
if (updatedNode.attrs.src !== currentNode.attrs.src) {
el.src = updatedNode.attrs.src || "";
el.src = normalizeFileUrl(updatedNode.attrs.src);
}
const w = updatedNode.attrs.width;
@@ -317,12 +318,14 @@ export const Excalidraw = Node.create<ExcalidrawOptions>({
});
}
// Hide until image loads
dom.style.visibility = "hidden";
// Show skeleton background while image loads from server
dom.style.pointerEvents = "none";
dom.style.background =
"light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))";
el.onload = () => {
dom.style.visibility = "";
dom.style.pointerEvents = "";
dom.style.background = "";
};
return nodeView;
+15 -5
View File
@@ -6,6 +6,7 @@ import {
Range,
ResizableNodeView,
} from "@tiptap/core";
import { normalizeFileUrl } from "../media-utils";
import type { ResizableNodeViewDirection } from "@tiptap/core";
export type ImageResizeOptions = {
@@ -252,11 +253,18 @@ export const TiptapImage = Image.extend<ImageOptions>({
}
});
el.src = HTMLAttributes.src;
el.src = normalizeFileUrl(HTMLAttributes.src);
el.style.display = "block";
el.style.maxWidth = "100%";
el.style.borderRadius = "8px";
if (typeof node.attrs.width === "number" && node.attrs.width > 0) {
el.style.width = `${node.attrs.width}px`;
if (typeof node.attrs.height === "number" && node.attrs.height > 0) {
el.style.height = `${node.attrs.height}px`;
}
}
let currentNode = node;
const nodeView = new ResizableNodeView({
@@ -287,7 +295,7 @@ export const TiptapImage = Image.extend<ImageOptions>({
}
if (updatedNode.attrs.src !== currentNode.attrs.src) {
el.src = updatedNode.attrs.src || "";
el.src = normalizeFileUrl(updatedNode.attrs.src);
}
if (updatedNode.attrs.alt !== currentNode.attrs.alt) {
@@ -352,12 +360,14 @@ export const TiptapImage = Image.extend<ImageOptions>({
});
}
// Hide until image loads (official TipTap pattern)
dom.style.visibility = "hidden";
// Show skeleton background while image loads from server
dom.style.pointerEvents = "none";
dom.style.background =
"light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))";
el.onload = () => {
dom.style.visibility = "";
dom.style.pointerEvents = "";
dom.style.background = "";
};
return nodeView;
@@ -1,5 +1,12 @@
import { Editor } from "@tiptap/core";
export function normalizeFileUrl(src: string): string {
if (src && src.startsWith("/files/")) {
return "/api" + src;
}
return src || "";
}
export type UploadFn = (
file: File,
editor: Editor,
+15 -5
View File
@@ -1,5 +1,6 @@
import { ReactNodeViewRenderer } from "@tiptap/react";
import { Range, Node, mergeAttributes, ResizableNodeView } from "@tiptap/core";
import { normalizeFileUrl } from "../media-utils";
import type { ResizableNodeViewDirection } from "@tiptap/core";
export type VideoResizeOptions = {
@@ -223,13 +224,20 @@ export const TiptapVideo = Node.create<VideoOptions>({
}
const el = document.createElement("video");
el.src = node.attrs.src;
el.src = normalizeFileUrl(node.attrs.src);
el.controls = true;
el.preload = "metadata";
el.style.display = "block";
el.style.maxWidth = "100%";
el.style.borderRadius = "8px";
if (typeof node.attrs.width === "number" && node.attrs.width > 0) {
el.style.width = `${node.attrs.width}px`;
if (typeof node.attrs.height === "number" && node.attrs.height > 0) {
el.style.height = `${node.attrs.height}px`;
}
}
let currentNode = node;
const nodeView = new ResizableNodeView({
@@ -260,7 +268,7 @@ export const TiptapVideo = Node.create<VideoOptions>({
}
if (updatedNode.attrs.src !== currentNode.attrs.src) {
el.src = updatedNode.attrs.src || "";
el.src = normalizeFileUrl(updatedNode.attrs.src);
}
const w = updatedNode.attrs.width;
@@ -318,12 +326,14 @@ export const TiptapVideo = Node.create<VideoOptions>({
});
}
// Hide until video metadata loads
dom.style.visibility = "hidden";
// Show skeleton background while video loads from server
dom.style.pointerEvents = "none";
dom.style.background =
"light-dark(var(--mantine-color-gray-2), var(--mantine-color-dark-6))";
el.onloadedmetadata = () => {
dom.style.visibility = "";
dom.style.pointerEvents = "";
dom.style.background = "";
};
return nodeView;