Files
kandev/apps/web/components/task-prompt-reference-node.tsx
T
2026-09-14 18:45:35 +01:00

96 lines
3.0 KiB
TypeScript

"use client";
import { Node, mergeAttributes } from "@tiptap/core";
import { NodeViewWrapper, ReactNodeViewRenderer, type ReactNodeViewProps } from "@tiptap/react";
import { IconX } from "@tabler/icons-react";
import { useTranslation } from "react-i18next";
import { PromptMentionChip } from "@/components/task/chat/messages/prompt-mention-components";
import { useTouchDrawer } from "@/hooks/use-compact-task-chrome";
import { useResponsiveBreakpoint } from "@/hooks/use-responsive-breakpoint";
import { cn } from "@/lib/utils";
export type TaskPromptReferenceAttrs = {
name: string;
value: string;
};
export const TaskPromptReference = Node.create({
name: "promptReference",
group: "inline",
inline: true,
atom: true,
selectable: true,
addAttributes() {
return {
name: { default: "" },
value: { default: "" },
};
},
parseHTML() {
return [{ tag: "span[data-task-prompt-reference]" }];
},
renderHTML({ HTMLAttributes }) {
return [
"span",
mergeAttributes({ "data-task-prompt-reference": "" }, HTMLAttributes),
HTMLAttributes.value || HTMLAttributes.name || "",
];
},
renderText({ node }) {
return String(node.attrs.value || node.attrs.name || "");
},
addNodeView() {
return ReactNodeViewRenderer(TaskPromptReferenceView);
},
});
function TaskPromptReferenceView({ node, deleteNode }: ReactNodeViewProps) {
const { t } = useTranslation();
const usesTouchDrawer = useTouchDrawer();
const { isMobile } = useResponsiveBreakpoint();
const usesTouchTarget = isMobile || usesTouchDrawer;
const attrs = node.attrs as TaskPromptReferenceAttrs;
const removeLabel = t("task:removeLabeled", { label: attrs.value });
return (
<NodeViewWrapper
as="span"
data-testid="task-prompt-reference"
data-prompt-name={attrs.name}
className={cn(
"inline-flex max-w-full min-w-0 box-border items-center gap-0.5 rounded-md border border-emerald-300/35 bg-emerald-400/20 px-1 align-baseline",
usesTouchTarget ? "min-h-11" : "h-6",
)}
>
<PromptMentionChip name={attrs.name} value={attrs.value} presentation="editable" />
<button
type="button"
data-testid="task-prompt-reference-remove"
aria-label={removeLabel}
title={removeLabel}
contentEditable={false}
className={cn(
"inline-flex h-5 w-5 shrink-0 cursor-pointer items-center justify-center rounded-sm text-muted-foreground transition-colors hover:bg-emerald-500/15 hover:text-foreground focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-emerald-500/80",
usesTouchTarget && "h-11 min-h-11 min-w-11 w-11",
)}
onMouseDown={(event) => {
event.preventDefault();
event.stopPropagation();
}}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
deleteNode();
}}
>
<IconX className="h-4 w-4" />
</button>
</NodeViewWrapper>
);
}