@@ -1412,31 +1412,109 @@ function normalizeFilePageIcon(icon: string | null | undefined) {
14121412 return trimmed . slice ( 0 , 8 ) ;
14131413}
14141414
1415- function getLinkResourceThumbnailUrl ( file : FileRecord ) {
1415+ interface LinkResourceCardMetadata {
1416+ faviconUrl : string | null ;
1417+ imageUrl : string | null ;
1418+ title : string | null ;
1419+ }
1420+
1421+ function isObjectRecord ( value : unknown ) : value is Record < string , unknown > {
1422+ return typeof value === "object" && value !== null && ! Array . isArray ( value ) ;
1423+ }
1424+
1425+ function readNonEmptyString ( value : unknown ) {
1426+ return typeof value === "string" && value . trim ( ) . length > 0
1427+ ? value . trim ( )
1428+ : null ;
1429+ }
1430+
1431+ function getLinkResourceCardMetadata (
1432+ file : FileRecord
1433+ ) : LinkResourceCardMetadata | null {
14161434 const metadata = file . metadata ;
1417- const link =
1418- metadata &&
1419- typeof metadata === "object" &&
1420- ! Array . isArray ( metadata ) &&
1421- metadata . link &&
1422- typeof metadata . link === "object" &&
1423- ! Array . isArray ( metadata . link )
1424- ? ( metadata . link as Record < string , unknown > )
1425- : null ;
1435+ const link = isObjectRecord ( metadata ) && isObjectRecord ( metadata . link )
1436+ ? metadata . link
1437+ : null ;
14261438 if ( ! link ) {
14271439 return null ;
14281440 }
14291441
1430- const snapshot =
1431- link . snapshot && typeof link . snapshot === "object"
1432- ? ( link . snapshot as Record < string , unknown > )
1433- : null ;
1442+ const sourceUrl = readNonEmptyString ( link . sourceUrl ) ;
1443+ if ( ! sourceUrl ) {
1444+ return null ;
1445+ }
1446+
1447+ const snapshot = isObjectRecord ( link . snapshot ) ? link . snapshot : null ;
14341448 const imageUrl =
1435- ( typeof snapshot ?. imageUrl === "string" && snapshot . imageUrl . trim ( ) ) ||
1436- ( typeof link . imageUrl === "string" && link . imageUrl . trim ( ) ) ||
1449+ readNonEmptyString ( snapshot ?. imageUrl ) ??
1450+ readNonEmptyString ( link . imageUrl ) ??
14371451 null ;
1452+ const pageIcon = normalizeFilePageIcon ( file . page ?. icon ) ;
1453+ const faviconUrl =
1454+ readNonEmptyString ( link . favicon ) ??
1455+ ( pageIcon && isRenderableIconUrl ( pageIcon )
1456+ ? pageIcon
1457+ : new URL ( "/favicon.ico" , sourceUrl ) . toString ( ) ) ;
14381458
1439- return imageUrl ;
1459+ return {
1460+ faviconUrl,
1461+ imageUrl,
1462+ title : readNonEmptyString ( link . title ) ,
1463+ } ;
1464+ }
1465+
1466+ function LinkResourceThumbnailFallback ( {
1467+ faviconUrl,
1468+ loading = false ,
1469+ } : {
1470+ faviconUrl : string | null ;
1471+ loading ?: boolean ;
1472+ } ) {
1473+ return (
1474+ < div className = "relative flex h-full w-full items-center justify-center overflow-hidden rounded-md border border-border/60 bg-muted/70" >
1475+ < LinkSimple
1476+ aria-hidden = "true"
1477+ className = "size-8 text-muted-foreground/60"
1478+ />
1479+ { faviconUrl ? (
1480+ < img
1481+ alt = ""
1482+ className = "absolute max-h-10 max-w-10 object-contain"
1483+ height = { 40 }
1484+ onError = { ( event ) => {
1485+ event . currentTarget . style . display = "none" ;
1486+ } }
1487+ referrerPolicy = "no-referrer"
1488+ src = { faviconUrl }
1489+ width = { 40 }
1490+ />
1491+ ) : null }
1492+ { loading ? (
1493+ < Spinner className = "absolute right-2 bottom-2 size-4 text-muted-foreground" />
1494+ ) : null }
1495+ </ div >
1496+ ) ;
1497+ }
1498+
1499+ function LinkResourceTitleIcon ( { faviconUrl } : { faviconUrl : string | null } ) {
1500+ return (
1501+ < span className = "relative inline-flex size-4 shrink-0 items-center justify-center overflow-hidden rounded-[3px] bg-muted text-muted-foreground" >
1502+ < LinkSimple aria-hidden = "true" className = "size-3" />
1503+ { faviconUrl ? (
1504+ < img
1505+ alt = ""
1506+ className = "absolute inset-0 size-full object-contain"
1507+ height = { 16 }
1508+ onError = { ( event ) => {
1509+ event . currentTarget . style . display = "none" ;
1510+ } }
1511+ referrerPolicy = "no-referrer"
1512+ src = { faviconUrl }
1513+ width = { 16 }
1514+ />
1515+ ) : null }
1516+ </ span >
1517+ ) ;
14401518}
14411519
14421520function isRenderableIconUrl ( icon : string ) {
@@ -1542,23 +1620,11 @@ function PendingLinkImportCard({ item }: { item: PendingLinkImport }) {
15421620 style = { { width : 160 } }
15431621 >
15441622 < CardContent className = "px-0 pt-0" >
1545- < div className = "relative flex h-28 items-center justify-center overflow-hidden rounded-md border border-border/60 bg-muted/70 " >
1546- < LinkSimple
1547- aria-hidden = "true"
1548- className = "size-8 text-muted-foreground/60"
1623+ < div className = "h-28" >
1624+ < LinkResourceThumbnailFallback
1625+ faviconUrl = { item . faviconUrl }
1626+ loading
15491627 />
1550- < img
1551- alt = ""
1552- className = "absolute max-h-10 max-w-10 object-contain"
1553- height = { 40 }
1554- onError = { ( event ) => {
1555- event . currentTarget . style . display = "none" ;
1556- } }
1557- referrerPolicy = "no-referrer"
1558- src = { item . faviconUrl }
1559- width = { 40 }
1560- />
1561- < Spinner className = "absolute right-2 bottom-2 size-4 text-muted-foreground" />
15621628 </ div >
15631629 < div className = "mt-2 flex min-w-0 items-center gap-2" >
15641630 < LinkSimple
@@ -1797,6 +1863,9 @@ export function FileExplorer({
17971863 const importLinkIntentVersion = useFilesUiStore (
17981864 ( state ) => state . intentVersion . importLink
17991865 ) ;
1866+ const consumedImportLinkIntentVersion = useFilesUiStore (
1867+ ( state ) => state . consumedIntentVersion . importLink
1868+ ) ;
18001869 const uploadFileIntentVersion = useFilesUiStore (
18011870 ( state ) => state . intentVersion . uploadFile
18021871 ) ;
@@ -1829,7 +1898,6 @@ export function FileExplorer({
18291898 deleteSelection : 0 ,
18301899 focusSearch : 0 ,
18311900 goParent : 0 ,
1832- importLink : 0 ,
18331901 moveSelectionUp : 0 ,
18341902 newNote : 0 ,
18351903 openSelection : 0 ,
@@ -3424,8 +3492,8 @@ export function FileExplorer({
34243492 openCreateNoteDialog ( currentFolderId ) ;
34253493 }
34263494
3427- if ( importLinkIntentVersion > processed . importLink ) {
3428- processed . importLink = importLinkIntentVersion ;
3495+ if ( importLinkIntentVersion > consumedImportLinkIntentVersion ) {
3496+ filesUiActions . consumeIntent ( " importLink" , importLinkIntentVersion ) ;
34293497 openImportLinkDialog ( currentFolderId ) ;
34303498 }
34313499
@@ -3452,6 +3520,7 @@ export function FileExplorer({
34523520 }
34533521 } , [
34543522 createFolderIntentVersion ,
3523+ consumedImportLinkIntentVersion ,
34553524 currentFolderId ,
34563525 focusSearchIntentVersion ,
34573526 importLinkIntentVersion ,
@@ -6913,8 +6982,14 @@ export function FileExplorer({
69136982 const fileKind = detectFileKind ( file ) ;
69146983 const fileCardType =
69156984 fileKind === "sheet" ? "document" : fileKind ;
6985+ const linkCardMetadata =
6986+ getLinkResourceCardMetadata ( file ) ;
69166987 const linkThumbnailUrl =
6917- getLinkResourceThumbnailUrl ( file ) ;
6988+ linkCardMetadata ?. imageUrl ?? null ;
6989+ const fileCardName = linkCardMetadata
6990+ ? ( linkCardMetadata . title ??
6991+ file . name . replace ( / \. m d x ? $ / i, "" ) )
6992+ : file . name ;
69186993 const searchResult = searchResultByFileId . get (
69196994 file . id
69206995 ) ;
@@ -7041,7 +7116,7 @@ export function FileExplorer({
70417116 }
70427117 matchMeta = { searchMatchMeta }
70437118 matchSnippet = { searchResult ?. snippet }
7044- name = { file . name }
7119+ name = { fileCardName }
70457120 previewContent = {
70467121 isImage ? (
70477122 < img
@@ -7074,6 +7149,13 @@ export function FileExplorer({
70747149 hoveredPreviewFileId === file . id
70757150 }
70767151 />
7152+ ) : linkCardMetadata &&
7153+ ! linkThumbnailUrl ? (
7154+ < LinkResourceThumbnailFallback
7155+ faviconUrl = {
7156+ linkCardMetadata . faviconUrl
7157+ }
7158+ />
70777159 ) : isMarkdown && ! linkThumbnailUrl ? (
70787160 < MarkdownThumbnail
70797161 className = "h-full w-full"
@@ -7088,7 +7170,15 @@ export function FileExplorer({
70887170 }
70897171 previewUrl = { linkThumbnailUrl ?? undefined }
70907172 titleIcon = {
7091- normalizeFilePageIcon ( file . page ?. icon )
7173+ linkCardMetadata ? (
7174+ < LinkResourceTitleIcon
7175+ faviconUrl = {
7176+ linkCardMetadata . faviconUrl
7177+ }
7178+ />
7179+ ) : normalizeFilePageIcon (
7180+ file . page ?. icon
7181+ )
70927182 ? getFileVisualIcon (
70937183 file ,
70947184 fileKind ,
0 commit comments