mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-05-02 21:50:10 +00:00
134 lines
3.2 KiB
TypeScript
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 },
|
|
});
|
|
}
|