mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-11 22:51:05 +00:00
174 lines
No EOL
4.4 KiB
TypeScript
174 lines
No EOL
4.4 KiB
TypeScript
import { AssetRecordType, Editor, TLAsset, TLAssetId, TLBookmarkShape, TLExternalContentSource, TLShapePartial, Vec, VecLike, createShapeId, getEmbedInfo, getHashForString } from "tldraw";
|
|
|
|
export default async function createEmbedsFromUrl({url, point, sources, editor}: {
|
|
url: string
|
|
point?: VecLike | undefined
|
|
sources?: TLExternalContentSource[] | undefined
|
|
editor: Editor
|
|
}){
|
|
|
|
const position =
|
|
point ??
|
|
(editor.inputs.shiftKey
|
|
? editor.inputs.currentPagePoint
|
|
: editor.getViewportPageBounds().center);
|
|
|
|
if (url?.includes("x.com") || url?.includes("twitter.com")) {
|
|
return editor.createShape({
|
|
type: "Twittercard",
|
|
x: position.x - 250,
|
|
y: position.y - 150,
|
|
props: { url: url },
|
|
});
|
|
|
|
}
|
|
|
|
// try to paste as an embed first
|
|
const embedInfo = getEmbedInfo(url);
|
|
|
|
if (embedInfo) {
|
|
return editor.putExternalContent({
|
|
type: "embed",
|
|
url: embedInfo.url,
|
|
point,
|
|
embed: embedInfo.definition,
|
|
});
|
|
}
|
|
|
|
const assetId: TLAssetId = AssetRecordType.createId(
|
|
getHashForString(url),
|
|
);
|
|
const shape = createEmptyBookmarkShape(editor, url, position);
|
|
|
|
// Use an existing asset if we have one, or else else create a new one
|
|
let asset = editor.getAsset(assetId) as TLAsset;
|
|
let shouldAlsoCreateAsset = false;
|
|
if (!asset) {
|
|
shouldAlsoCreateAsset = true;
|
|
try {
|
|
const bookmarkAsset = await editor.getAssetForExternalContent({
|
|
type: "url",
|
|
url,
|
|
});
|
|
const fetchWebsite: {
|
|
title?: string;
|
|
image?: string;
|
|
description?: string;
|
|
} = await (await fetch(`/api/unfirlsite?website=${url}`, {
|
|
method: "POST"
|
|
})).json()
|
|
if (bookmarkAsset){
|
|
if (fetchWebsite.title) bookmarkAsset.props.title = fetchWebsite.title;
|
|
if (fetchWebsite.image) bookmarkAsset.props.image = fetchWebsite.image;
|
|
if (fetchWebsite.description) bookmarkAsset.props.description = fetchWebsite.description;
|
|
}
|
|
if (!bookmarkAsset) throw Error("Could not create an asset");
|
|
asset = bookmarkAsset;
|
|
} catch (e) {
|
|
console.log(e)
|
|
return;
|
|
}
|
|
}
|
|
|
|
editor.batch(() => {
|
|
if (shouldAlsoCreateAsset) {
|
|
editor.createAssets([asset]);
|
|
}
|
|
|
|
editor.updateShapes([
|
|
{
|
|
id: shape.id,
|
|
type: shape.type,
|
|
props: {
|
|
assetId: asset.id,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
}
|
|
|
|
function isURL(str: string) {
|
|
try {
|
|
new URL(str);
|
|
return true;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
|
|
export function handleExternalDroppedContent({text, editor}: {text:string, editor: Editor}){
|
|
const position = editor.inputs.shiftKey
|
|
? editor.inputs.currentPagePoint
|
|
: editor.getViewportPageBounds().center;
|
|
|
|
if (isURL(text)){
|
|
createEmbedsFromUrl({editor, url: text})
|
|
} else{
|
|
editor.createShape({
|
|
type: "text",
|
|
x: position.x - 75,
|
|
y: position.y - 75,
|
|
props: {
|
|
text: text,
|
|
size: "s",
|
|
textAlign: "start",
|
|
},
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
function centerSelectionAroundPoint(editor: Editor, position: VecLike) {
|
|
// Re-position shapes so that the center of the group is at the provided point
|
|
const viewportPageBounds = editor.getViewportPageBounds()
|
|
let selectionPageBounds = editor.getSelectionPageBounds()
|
|
|
|
if (selectionPageBounds) {
|
|
const offset = selectionPageBounds!.center.sub(position)
|
|
|
|
editor.updateShapes(
|
|
editor.getSelectedShapes().map((shape) => {
|
|
const localRotation = editor.getShapeParentTransform(shape).decompose().rotation
|
|
const localDelta = Vec.Rot(offset, -localRotation)
|
|
return {
|
|
id: shape.id,
|
|
type: shape.type,
|
|
x: shape.x! - localDelta.x,
|
|
y: shape.y! - localDelta.y,
|
|
}
|
|
})
|
|
)
|
|
}
|
|
|
|
// Zoom out to fit the shapes, if necessary
|
|
selectionPageBounds = editor.getSelectionPageBounds()
|
|
if (selectionPageBounds && !viewportPageBounds.contains(selectionPageBounds)) {
|
|
editor.zoomToSelection()
|
|
}
|
|
}
|
|
|
|
export function createEmptyBookmarkShape(
|
|
editor: Editor,
|
|
url: string,
|
|
position: VecLike
|
|
): TLBookmarkShape {
|
|
const partial: TLShapePartial = {
|
|
id: createShapeId(),
|
|
type: 'bookmark',
|
|
x: position.x - 150,
|
|
y: position.y - 160,
|
|
opacity: 1,
|
|
props: {
|
|
assetId: null,
|
|
url,
|
|
},
|
|
}
|
|
|
|
editor.batch(() => {
|
|
editor.createShapes([partial]).select(partial.id)
|
|
centerSelectionAroundPoint(editor, position)
|
|
})
|
|
|
|
return editor.getShape(partial.id) as TLBookmarkShape
|
|
} |