feat(editor): add alt text support for images (#2097)

* feat(editor): add alt text support for images
* feat:  extend alt text support to videos and diagrams

---------
Co-authored-by: Philipinho <16838612+Philipinho@users.noreply.github.com>
This commit is contained in:
Olivier Lambert
2026-05-20 17:45:59 +02:00
committed by GitHub
parent 66a754c9eb
commit 1c166c4736
12 changed files with 315 additions and 17 deletions
@@ -20,6 +20,7 @@ import {
import { useTranslation } from "react-i18next";
import { getFileUrl } from "@/lib/config.ts";
import { uploadImageAction } from "@/features/editor/components/image/upload-image-action.tsx";
import { useAltTextControl } from "@/features/editor/components/common/use-alt-text-control.tsx";
import classes from "../common/toolbar-menu.module.css";
export function ImageMenu({ editor }: EditorMenuProps) {
@@ -41,6 +42,7 @@ export function ImageMenu({ editor }: EditorMenuProps) {
isAlignCenter: ctx.editor.isActive("image", { align: "center" }),
isAlignRight: ctx.editor.isActive("image", { align: "right" }),
src: imageAttrs?.src || null,
alt: imageAttrs?.alt || "",
};
},
});
@@ -136,6 +138,16 @@ export function ImageMenu({ editor }: EditorMenuProps) {
editor.commands.deleteSelection();
}, [editor]);
const {
button: altTextButton,
panel: altTextPanel,
isEditing: isEditingAlt,
} = useAltTextControl({
editor,
nodeName: "image",
currentAlt: editorState?.alt || "",
});
return (
<BaseBubbleMenu
editor={editor}
@@ -149,7 +161,10 @@ export function ImageMenu({ editor }: EditorMenuProps) {
}}
shouldShow={shouldShow}
>
<div className={classes.toolbar}>
{isEditingAlt ? (
altTextPanel
) : (
<div className={classes.toolbar}>
<Tooltip position="top" label={t("Align left")} withinPortal={false}>
<ActionIcon
onClick={alignImageLeft}
@@ -188,6 +203,10 @@ export function ImageMenu({ editor }: EditorMenuProps) {
<div className={classes.divider} />
{altTextButton}
<div className={classes.divider} />
<Tooltip position="top" label={t("Download")} withinPortal={false}>
<ActionIcon
onClick={handleDownload}
@@ -220,7 +239,8 @@ export function ImageMenu({ editor }: EditorMenuProps) {
<IconTrash size={18} />
</ActionIcon>
</Tooltip>
</div>
</div>
)}
<input
ref={fileInputRef}
@@ -9,7 +9,7 @@ import { useTranslation } from "react-i18next";
export default function ImageView(props: NodeViewProps) {
const { t } = useTranslation();
const { editor, node, selected } = props;
const { src, width, align, title, aspectRatio, placeholder } = node.attrs;
const { src, width, align, alt, aspectRatio, placeholder } = node.attrs;
const alignClass = useMemo(() => {
if (align === "left") return "alignLeft";
if (align === "right") return "alignRight";
@@ -42,7 +42,7 @@ export default function ImageView(props: NodeViewProps) {
}}
>
{src && (
<Image radius="md" fit="contain" src={getFileUrl(src)} alt={title} />
<Image radius="md" fit="contain" src={getFileUrl(src)} alt={alt} />
)}
{!src && previewSrc && (
<Group pos="relative" h="100%" w="100%">