diff options
| author | Dhravya Shah <[email protected]> | 2024-06-18 17:58:46 -0500 |
|---|---|---|
| committer | Dhravya Shah <[email protected]> | 2024-06-18 17:58:46 -0500 |
| commit | f4bb71e8f7e07bb2e919b7f222d5acb2905eb8f2 (patch) | |
| tree | 7310dc521ef3559055bbe71f50c3861be2fa0503 /apps/web/app/(canvas)/lib | |
| parent | darkmode by default - so that the colors don't f up on lightmode devices (diff) | |
| parent | Create Embeddings for Canvas (diff) | |
| download | supermemory-default-darkmode.tar.xz supermemory-default-darkmode.zip | |
Diffstat (limited to 'apps/web/app/(canvas)/lib')
| -rw-r--r-- | apps/web/app/(canvas)/lib/createAssetUrl.ts | 94 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/lib/createEmbeds.ts | 142 | ||||
| -rw-r--r-- | apps/web/app/(canvas)/lib/loadSnap.ts | 13 |
3 files changed, 249 insertions, 0 deletions
diff --git a/apps/web/app/(canvas)/lib/createAssetUrl.ts b/apps/web/app/(canvas)/lib/createAssetUrl.ts new file mode 100644 index 00000000..05c2baea --- /dev/null +++ b/apps/web/app/(canvas)/lib/createAssetUrl.ts @@ -0,0 +1,94 @@ +import { + AssetRecordType, + TLAsset, + getHashForString, + truncateStringWithEllipsis, +} from "tldraw"; +// import { BOOKMARK_ENDPOINT } from './config' + +interface ResponseBody { + title?: string; + description?: string; + image?: string; +} + +export async function createAssetFromUrl({ + url, +}: { + type: "url"; + url: string; +}): Promise<TLAsset> { + // try { + // // First, try to get the meta data from our endpoint + // const meta = (await ( + // await fetch(BOOKMARK_ENDPOINT, { + // method: 'POST', + // headers: { + // 'Content-Type': 'application/json', + // }, + // body: JSON.stringify({ + // url, + // }), + // }) + // ).json()) as ResponseBody + + // return { + // id: AssetRecordType.createId(getHashForString(url)), + // typeName: 'asset', + // type: 'bookmark', + // props: { + // src: url, + // description: meta.description ?? '', + // image: meta.image ?? '', + // title: meta.title ?? truncateStringWithEllipsis(url, 32), + // }, + // meta: {}, + // } + // } catch (error) { + // Otherwise, fallback to fetching data from the url + + let meta: { image: string; title: string; description: string }; + + try { + const resp = await fetch(url, { method: "GET", mode: "no-cors" }); + const html = await resp.text(); + const doc = new DOMParser().parseFromString(html, "text/html"); + meta = { + image: + doc.head + .querySelector('meta[property="og:image"]') + ?.getAttribute("content") ?? "", + title: + doc.head + .querySelector('meta[property="og:title"]') + ?.getAttribute("content") ?? truncateStringWithEllipsis(url, 32), + description: + doc.head + .querySelector('meta[property="og:description"]') + ?.getAttribute("content") ?? "", + }; + } catch (error) { + console.error(error); + meta = { + image: "", + title: truncateStringWithEllipsis(url, 32), + description: "", + }; + } + + // Create the bookmark asset from the meta + return { + id: AssetRecordType.createId(getHashForString(url)), + typeName: "asset", + type: "bookmark", + props: { + src: url, + image: meta.image, + title: meta.title, + description: meta.description, + favicon: meta.image, + }, + meta: {}, + }; + // } +} diff --git a/apps/web/app/(canvas)/lib/createEmbeds.ts b/apps/web/app/(canvas)/lib/createEmbeds.ts new file mode 100644 index 00000000..53d81533 --- /dev/null +++ b/apps/web/app/(canvas)/lib/createEmbeds.ts @@ -0,0 +1,142 @@ +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 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 +}
\ No newline at end of file diff --git a/apps/web/app/(canvas)/lib/loadSnap.ts b/apps/web/app/(canvas)/lib/loadSnap.ts new file mode 100644 index 00000000..15aad998 --- /dev/null +++ b/apps/web/app/(canvas)/lib/loadSnap.ts @@ -0,0 +1,13 @@ +import { createTLStore, defaultShapeUtils } from "tldraw"; +import { twitterCardUtil } from "../twitterCard"; +export async function loadRemoteSnapshot() { + const res = await fetch( + "https://learning-cf.pruthvirajthinks.workers.dev/get/page3", + ); + const snapshot = JSON.parse(await res.json()); + const newStore = createTLStore({ + shapeUtils: [...defaultShapeUtils, twitterCardUtil], + }); + newStore.loadSnapshot(snapshot); + return newStore; +}
\ No newline at end of file |