aboutsummaryrefslogtreecommitdiff
path: root/apps/web/app/(canvas)/lib
diff options
context:
space:
mode:
authorDhravya Shah <[email protected]>2024-06-18 17:58:46 -0500
committerDhravya Shah <[email protected]>2024-06-18 17:58:46 -0500
commitf4bb71e8f7e07bb2e919b7f222d5acb2905eb8f2 (patch)
tree7310dc521ef3559055bbe71f50c3861be2fa0503 /apps/web/app/(canvas)/lib
parentdarkmode by default - so that the colors don't f up on lightmode devices (diff)
parentCreate Embeddings for Canvas (diff)
downloadsupermemory-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.ts94
-rw-r--r--apps/web/app/(canvas)/lib/createEmbeds.ts142
-rw-r--r--apps/web/app/(canvas)/lib/loadSnap.ts13
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