2022-11-23 17:17:54 +00:00
< script setup lang = "ts" >
2023-01-08 06:21:09 +00:00
import type { mastodon } from 'masto'
2022-11-23 17:17:54 +00:00
2022-12-14 23:30:54 +00:00
const props = withDefaults ( defineProps < {
2023-01-08 06:21:09 +00:00
attachment : mastodon . v1 . MediaAttachment
2022-11-23 17:17:54 +00:00
alt ? : string
removable ? : boolean
2022-12-16 19:55:31 +00:00
dialogLabelledBy ? : string
2022-11-23 17:17:54 +00:00
} > ( ) , {
removable : true ,
} )
2022-12-23 14:08:10 +00:00
const emit = defineEmits < {
2022-11-23 17:17:54 +00:00
( evt : 'remove' ) : void
2022-12-14 23:30:54 +00:00
( evt : 'setDescription' , description : string ) : void
2022-11-23 17:17:54 +00:00
} > ( )
2022-12-14 23:30:54 +00:00
2023-03-06 13:12:31 +00:00
// from https://github.com/mastodon/mastodon/blob/dfa984/app/models/media_attachment.rb#L40
const maxDescriptionLength = 1500
2022-12-14 23:30:54 +00:00
const isEditDialogOpen = ref ( false )
2024-02-26 13:11:21 +00:00
const description = ref ( props . attachment . description ? ? '' )
2024-05-27 20:02:06 +01:00
const generationInProgress = ref ( false )
2024-05-28 18:46:04 +01:00
const userSettings = useUserSettings ( )
2024-05-27 20:02:06 +01:00
async function generateAltText ( ) {
// eslint-disable-next-line no-console
console . log ( JSON . parse ( JSON . stringify ( props ) ) )
const url = props . attachment . url
if ( ! url )
return
if ( generationInProgress . value )
return
2024-05-28 18:46:04 +01:00
const experimentalAltTextGeneration = getPreferences ( userSettings . value , 'experimentalAltTextGeneration' )
if ( ! experimentalAltTextGeneration ) {
// TODO @Shinigami92 2024-05-28: Use a fancy dialog instead of the browser's alert
// eslint-disable-next-line no-alert
const allow = confirm ( 'This will download a model with ~250MiB. Do you want to continue? This is an experimental feature and might fail in several scenarios.' )
if ( ! allow )
return
togglePreferences ( 'experimentalAltTextGeneration' )
}
2024-05-27 20:02:06 +01:00
generationInProgress . value = true
try {
const { pipeline , RawImage } = await import ( '@xenova/transformers' )
const pipe = await pipeline ( 'image-to-text' , 'Xenova/vit-gpt2-image-captioning' )
const imageElement = new Image ( )
2024-05-28 18:46:04 +01:00
// See https://www.hacksoft.io/blog/handle-images-cors-error-in-chrome for why using `?request-with-cors`
2024-05-27 20:02:06 +01:00
imageElement . crossOrigin = 'Anonymous'
imageElement . src = ` ${ url } ?request-with-cors `
const dataUrl = new Promise < string > ( ( resolve ) => {
imageElement . onload = ( ) => {
const canvas = document . createElement ( 'canvas' )
canvas . width = imageElement . width
canvas . height = imageElement . height
const ctx = canvas . getContext ( '2d' ) !
ctx . drawImage ( imageElement , 0 , 0 )
// TODO @Shinigami92 2024-05-28: Fix "Uncaught DOMException: Failed to execute 'toDataURL' on 'HTMLCanvasElement': Tainted canvases may not be exported."
const dataUrl = canvas . toDataURL ( ` image/ ${ url . split ( '.' ) . pop ( ) ! } ` )
resolve ( dataUrl )
}
} )
const img = await RawImage . fromURL ( await dataUrl )
const out = await pipe ( img )
// eslint-disable-next-line no-console
console . debug ( out )
const firstOut = out ? . [ 0 ]
if ( ! firstOut || Array . isArray ( firstOut ) )
return
description . value = firstOut . generated _text
}
catch ( error ) {
console . error ( error )
// TODO @Shinigami92 2024-05-27: Display error message to the user, so they know that something went wrong
}
finally {
generationInProgress . value = false
}
}
2023-03-30 20:01:24 +01:00
function toggleApply ( ) {
2022-12-23 14:08:10 +00:00
isEditDialogOpen . value = false
2024-02-21 15:20:08 +00:00
emit ( 'setDescription' , description . value )
2022-12-23 14:08:10 +00:00
}
2022-11-23 17:17:54 +00:00
< / script >
< template >
< div relative group >
2023-02-15 10:34:23 +00:00
< StatusAttachment :attachment = "attachment" w -full is -preview / >
2022-11-24 04:05:13 +00:00
< div absolute right -2 top -2 >
< div
v - if = "removable"
2022-12-22 10:59:25 +00:00
: aria - label = "$t('attachment.remove_label')"
2023-01-21 10:52:36 +00:00
class = "bg-black/75 hover:bg-red/75"
text - white px2 py2 rounded - full cursor - pointer
2022-11-24 04:05:13 +00:00
@ click = "$emit('remove')"
>
2023-01-30 10:58:18 +00:00
< div i -ri : close -line text -3 text -6 md : text -3 / >
2022-11-24 04:05:13 +00:00
< / div >
2022-11-23 17:17:54 +00:00
< / div >
2022-12-14 23:30:54 +00:00
< div absolute right -2 bottom -2 >
< button class = "bg-black/75" text -white px2 py1 rounded -2 @ click = "isEditDialogOpen = true" >
2022-12-22 10:59:25 +00:00
{ { $t ( 'action.edit' ) } }
2022-12-14 23:30:54 +00:00
< / button >
< / div >
2022-12-16 19:55:31 +00:00
< ModalDialog
v - model = "isEditDialogOpen"
: dialog - labelled - by = "dialogLabelledBy"
py - 6
px - 6 max - w - 300
>
2022-12-23 09:09:52 +00:00
< div flex flex -col -reverse gap -5 md : flex -row >
2022-12-14 23:30:54 +00:00
< div flex flex -col gap -2 justify -between >
2022-12-16 19:55:31 +00:00
< h1 id = "edit-attachment" font -bold >
2022-12-22 10:59:25 +00:00
{ { $t ( 'attachment.edit_title' ) } }
2022-12-14 23:30:54 +00:00
< / h1 >
< div flex flex -col gap -2 >
2022-12-23 09:09:52 +00:00
< textarea v-model = "description" p -3 h -50 bg -base rounded -2 border -strong border -1 md : w -100 / >
2023-03-06 13:12:31 +00:00
< div flex flex -row -reverse >
< PublishCharacterCounter :length = "description.length" :max = "maxDescriptionLength" / >
< / div >
2024-05-27 20:02:06 +01:00
<!-- TODO @ Shinigami92 2024 - 05 - 27 : Style the button in the upper right corner of the textarea -- >
< button type = "button" btn -outline flex = "~ gap2 center" :disabled = "generationInProgress" @click ="generateAltText" >
< span block i -ri : sparkling -2 -line / >
{ { $t ( 'action.generate-alt-text' ) } }
< span v-if = "generationInProgress" aria-hidden="true" block animate -spin preserve -3d >
< span block i -ri : loader -2 -fill aria -hidden = " true " / >
< / span >
< / button >
2023-03-06 13:12:31 +00:00
< button btn -outline : disabled = "description.length > maxDescriptionLength" @click ="toggleApply" >
2022-12-22 10:59:25 +00:00
{ { $t ( 'action.apply' ) } }
2022-12-14 23:30:54 +00:00
< / button >
< / div >
< button btn -outline @ click = "isEditDialogOpen = false" >
2022-12-22 10:59:25 +00:00
{ { $t ( 'action.close' ) } }
2022-12-14 23:30:54 +00:00
< / button >
< / div >
2023-02-15 10:34:23 +00:00
< StatusAttachment :attachment = "attachment" w -full is -preview / >
2022-12-14 23:30:54 +00:00
< / div >
< / ModalDialog >
2022-11-23 17:17:54 +00:00
< / div >
< / template >