{
  "version": 3,
  "sources": ["../../../../src/lib/shapes/bookmark/bookmarks.ts"],
  "sourcesContent": ["import {\n\tAssetRecordType,\n\tEditor,\n\tResult,\n\tTLAssetId,\n\tTLBookmarkAsset,\n\tTLBookmarkShape,\n\tTLShapeId,\n\tTLShapePartial,\n\tcreateShapeId,\n\tdebounce,\n\tgetHashForString,\n} from '@tldraw/editor'\n\nexport const BOOKMARK_WIDTH = 300\nexport const BOOKMARK_HEIGHT = 320\nexport const BOOKMARK_JUST_URL_HEIGHT = 46\nconst SHORT_BOOKMARK_HEIGHT = 101\n\nexport function getBookmarkHeight(editor: Editor, assetId?: TLAssetId | null) {\n\tconst asset = (assetId ? editor.getAsset(assetId) : null) as TLBookmarkAsset | null\n\n\tif (asset) {\n\t\tif (!asset.props.image) {\n\t\t\tif (!asset.props.title) {\n\t\t\t\treturn BOOKMARK_JUST_URL_HEIGHT\n\t\t\t} else {\n\t\t\t\treturn SHORT_BOOKMARK_HEIGHT\n\t\t\t}\n\t\t}\n\t}\n\n\treturn BOOKMARK_HEIGHT\n}\n\nexport function setBookmarkHeight(editor: Editor, shape: TLBookmarkShape) {\n\treturn {\n\t\t...shape,\n\t\tprops: { ...shape.props, h: getBookmarkHeight(editor, shape.props.assetId) },\n\t}\n}\n\n/** @internal */\nexport function getHumanReadableAddress(url: string) {\n\ttry {\n\t\tconst objUrl = new URL(url)\n\t\t// we want the hostname without any www\n\t\treturn objUrl.hostname.replace(/^www\\./, '')\n\t} catch {\n\t\treturn url\n\t}\n}\n\nexport function updateBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmarkShape) {\n\tconst { url } = shape.props\n\n\t// Derive the asset id from the URL\n\tconst assetId: TLAssetId = AssetRecordType.createId(getHashForString(url))\n\n\tif (editor.getAsset(assetId)) {\n\t\t// Existing asset for this URL?\n\t\tif (shape.props.assetId !== assetId) {\n\t\t\teditor.updateShapes([\n\t\t\t\t{\n\t\t\t\t\tid: shape.id,\n\t\t\t\t\ttype: shape.type,\n\t\t\t\t\tprops: { assetId },\n\t\t\t\t},\n\t\t\t])\n\t\t}\n\t} else {\n\t\t// No asset for this URL?\n\n\t\t// First, clear out the existing asset reference\n\t\teditor.updateShapes([\n\t\t\t{\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tprops: { assetId: null },\n\t\t\t},\n\t\t])\n\n\t\t// Then try to asyncronously create a new one\n\t\tcreateBookmarkAssetOnUrlChange(editor, shape)\n\t}\n}\n\n/**\n * Resolve the asset id to render for a bookmark. Bookmark assets are keyed\n * deterministically by URL, so if the shape has no `assetId` but an asset for\n * its URL already exists in the store, use that.\n *\n * This keeps a bookmark from staying stuck on its placeholder after the shape\n * is recreated outside of the hydration flow \u2014 e.g. on redo, where the\n * placeholder is restored with a null `assetId` but its hydrated asset (created\n * with `history: 'ignore'`) still lives in the store.\n */\nexport function getResolvedBookmarkAssetId(\n\teditor: Editor,\n\tassetId: TLAssetId | null,\n\turl: string\n): TLAssetId | null {\n\tif (assetId) return assetId\n\tif (!url) return null\n\n\tconst derivedId: TLAssetId = AssetRecordType.createId(getHashForString(url))\n\treturn editor.getAsset(derivedId) ? derivedId : null\n}\n\n/**\n * The effective height to render and measure a bookmark at.\n *\n * Normally this is the stored `props.h`. But when the shape's `assetId` resolves\n * to a different asset than it stores \u2014 e.g. a placeholder restored on redo with\n * a null `assetId` whose asset already exists in the store \u2014 `props.h` is stale\n * (it still holds the placeholder height). In that case recompute the height\n * from the resolved asset so rendering, the indicator, and the geometry/selection\n * bounds all stay in sync.\n */\nexport function getBookmarkShapeHeight(editor: Editor, shape: TLBookmarkShape): number {\n\tconst resolvedAssetId = getResolvedBookmarkAssetId(editor, shape.props.assetId, shape.props.url)\n\tif (resolvedAssetId === shape.props.assetId) return shape.props.h\n\treturn getBookmarkHeight(editor, resolvedAssetId)\n}\n\nasync function _createBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmarkShape) {\n\tif (editor.isDisposed) return\n\n\tconst { url } = shape.props\n\n\t// Create the asset using the external content manager's createAssetFromUrl method.\n\t// This may be overwritten by the user (for example, we overwrite it on tldraw.com)\n\tconst asset = await editor.getAssetForExternalContent({ type: 'url', url })\n\n\tif (!asset) {\n\t\t// No asset? Just leave the bookmark as a null assetId.\n\t\treturn\n\t}\n\n\teditor.run(() => {\n\t\t// Create the new asset\n\t\teditor.createAssets([asset])\n\n\t\t// And update the shape\n\t\teditor.updateShapes([\n\t\t\t{\n\t\t\t\tid: shape.id,\n\t\t\t\ttype: shape.type,\n\t\t\t\tprops: { assetId: asset.id },\n\t\t\t},\n\t\t])\n\t})\n}\n\nconst bookmarkDebouncers = new WeakMap<\n\tEditor,\n\tReturnType<typeof debounce<[Editor, TLBookmarkShape], void>>\n>()\n\nfunction createBookmarkAssetOnUrlChange(editor: Editor, shape: TLBookmarkShape) {\n\tlet fn = bookmarkDebouncers.get(editor)\n\tif (!fn) {\n\t\tfn = debounce(_createBookmarkAssetOnUrlChange, 500)\n\t\tbookmarkDebouncers.set(editor, fn)\n\t}\n\tfn(editor, shape)\n}\n\n/**\n * Creates a bookmark shape from a URL.\n *\n * The shape is created immediately as a placeholder so the user gets visible\n * feedback at the paste location, and the bookmark metadata (title, description,\n * image, favicon) is fetched in the background. Once metadata resolves, the\n * shape is patched with the resulting asset. If the fetch fails, the shape is\n * left as a URL-only bookmark.\n *\n * @returns A Result containing the created bookmark shape or an error\n * @public\n */\nexport async function createBookmarkFromUrl(\n\teditor: Editor,\n\t{\n\t\turl,\n\t\tcenter = editor.getViewportPageBounds().center,\n\t}: {\n\t\turl: string\n\t\tcenter?: { x: number; y: number }\n\t}\n): Promise<Result<TLBookmarkShape, string>> {\n\ttry {\n\t\t// If we already have a bookmark asset for this URL (e.g. another bookmark\n\t\t// shape was created from the same URL earlier), use it immediately rather\n\t\t// than re-fetching.\n\t\tconst expectedAssetId: TLAssetId = AssetRecordType.createId(getHashForString(url))\n\t\tconst existingAsset = editor.getAsset(expectedAssetId) as TLBookmarkAsset | null\n\n\t\tconst shapeId = createShapeId()\n\t\tconst shapePartial: TLShapePartial<TLBookmarkShape> = {\n\t\t\tid: shapeId,\n\t\t\ttype: 'bookmark',\n\t\t\tx: center.x - BOOKMARK_WIDTH / 2,\n\t\t\ty: center.y - BOOKMARK_HEIGHT / 2,\n\t\t\trotation: 0,\n\t\t\topacity: 1,\n\t\t\tprops: {\n\t\t\t\turl,\n\t\t\t\tassetId: existingAsset?.id ?? null,\n\t\t\t\tw: BOOKMARK_WIDTH,\n\t\t\t\th: getBookmarkHeight(editor, existingAsset?.id),\n\t\t\t},\n\t\t}\n\n\t\teditor.run(() => {\n\t\t\teditor.createShapes([shapePartial])\n\t\t})\n\n\t\tconst createdShape = editor.getShape<TLBookmarkShape>(shapeId)\n\t\tif (!createdShape) {\n\t\t\treturn Result.err('Failed to create bookmark shape')\n\t\t}\n\n\t\t// If we already had the asset, we're done \u2014 no metadata fetch needed.\n\t\tif (existingAsset) {\n\t\t\treturn Result.ok(createdShape)\n\t\t}\n\n\t\t// Otherwise kick off the metadata fetch in the background. The shape is\n\t\t// already visible as a placeholder; once the asset resolves we'll patch\n\t\t// the shape with its assetId.\n\t\tvoid hydrateBookmarkShape(editor, shapeId, url)\n\n\t\treturn Result.ok(createdShape)\n\t} catch (error) {\n\t\treturn Result.err(error instanceof Error ? error.message : 'Failed to create bookmark')\n\t}\n}\n\nasync function hydrateBookmarkShape(editor: Editor, shapeId: TLShapeId, url: string) {\n\tlet asset\n\ttry {\n\t\tasset = await editor.getAssetForExternalContent({ type: 'url', url })\n\t} catch (error) {\n\t\tconsole.error(error)\n\t\treturn\n\t}\n\n\tif (editor.isDisposed) return\n\tif (!asset) return\n\n\t// The shape may have been deleted (e.g. via undo), had its URL changed, or\n\t// had its asset replaced before the fetch resolved. In any of those cases we\n\t// don't want to attach metadata for the old URL.\n\tconst shape = editor.getShape<TLBookmarkShape>(shapeId)\n\tif (!shape) return\n\tif (shape.props.url !== url) return\n\tif (shape.props.assetId) return\n\n\teditor.run(\n\t\t() => {\n\t\t\tif (!editor.getAsset(asset.id)) {\n\t\t\t\teditor.createAssets([asset])\n\t\t\t}\n\t\t\teditor.updateShapes([\n\t\t\t\t{\n\t\t\t\t\tid: shapeId,\n\t\t\t\t\ttype: 'bookmark',\n\t\t\t\t\tprops: { assetId: asset.id },\n\t\t\t\t},\n\t\t\t])\n\t\t},\n\t\t{ history: 'ignore' }\n\t)\n}\n"],
  "mappings": "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",
  "names": []
}
