supermemory/apps/web/lib/ExternalDroppedContent.ts
Dhravya Shah a3f551e557 merged
2024-07-28 10:58:22 -07:00

134 lines
3.2 KiB
TypeScript

import { Editor } from "tldraw";
import createEmbedsFromUrl from "./createEmbeds";
function processURL(input: string): string | null {
let str = input.trim();
if (!/^(?:f|ht)tps?:\/\//i.test(str)) {
str = "http://" + str;
}
try {
const url = new URL(str);
return url.href;
} catch {
return str.match(
/^(https?:\/\/)?(www\.)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(\/.*)?$/i,
)
? str
: null;
}
}
function formatContent(title: string, content: string): string {
const totalLength = title.length + content.length;
const totalHeight = Math.ceil(totalLength * (2 / 3));
const titleLines = Math.ceil(totalHeight * (2 / 5));
const contentLines = totalHeight - titleLines;
const titleWithNewLines = title + "\n".repeat(titleLines);
const contentWithNewLines = content + "\n".repeat(contentLines);
return `${titleWithNewLines.trim()}\n\n${contentWithNewLines.trim()}`;
}
function formatTextToRatio(text: string): { height: number; width: number } {
const RATIO = 4 / 3;
const FONT_SIZE = 15;
const CHAR_WIDTH = FONT_SIZE * 0.6;
const LINE_HEIGHT = FONT_SIZE * 1.2;
const MIN_WIDTH = 200;
let width = Math.min(
800,
Math.max(MIN_WIDTH, Math.ceil(text.length * CHAR_WIDTH)),
);
width = Math.ceil(width / 4) * 4;
const maxLineWidth = Math.floor(width / CHAR_WIDTH);
const words = text.split(" ");
let lines: string[] = [];
let currentLine = "";
words.forEach((word) => {
if ((currentLine + word).length <= maxLineWidth) {
currentLine += (currentLine ? " " : "") + word;
} else {
lines.push(currentLine);
currentLine = word;
}
});
if (currentLine) {
lines.push(currentLine);
}
let height = Math.ceil(lines.length * LINE_HEIGHT);
if (width / height > RATIO) {
width = Math.ceil(height * RATIO);
} else {
height = Math.ceil(width / RATIO);
}
return { height, width };
}
type CardData = {
title: string;
type: string;
content: string;
text: boolean;
};
type DroppedData = CardData | string | { imageUrl: string };
export function handleExternalDroppedContent({
droppedData,
editor,
}: {
droppedData: DroppedData;
editor: Editor;
}) {
const position = editor.inputs.shiftKey
? editor.inputs.currentPagePoint
: editor.getViewportPageBounds().center;
if (typeof droppedData === "string") {
const processedURL = processURL(droppedData);
if (processedURL) {
createEmbedsFromUrl({ editor, url: processedURL });
} else {
createTextCard(editor, position, droppedData, "String Content");
}
} else if ("imageUrl" in droppedData) {
} else {
const { content, title, type, text } = droppedData;
if (text) {
const { height, width } = formatTextToRatio(content);
editor.createShape({
type: "text",
x: position.x - width / 2,
y: position.y - height / 2,
props: { text: formatContent(title, content) },
});
} else {
createTextCard(editor, position, title, type, content);
}
}
}
function createTextCard(
editor: Editor,
position: { x: number; y: number },
content: string,
type: string,
extraInfo: string = "",
) {
const { height, width } = formatTextToRatio(content);
editor.createShape({
type: "Textcard",
x: position.x - width / 2,
y: position.y - height / 2,
props: { content, type, extrainfo: extraInfo, h: 200, w: 400 },
});
}