import { isClient, useClipboard } from '@vueuse/core'
import { computed, ref, watchEffect } from 'vue'
import { useRoute } from 'vue-router'
/**
* Composable for copying raw Markdown content of the current post.
* Requires `siteConfig.llms.files: true` to have .md files available at build output.
*
* The `available` ref is initially `false` and becomes `true` after a HEAD request
* confirms the `.md` file exists. This allows themes to conditionally render
* the copy button only when the llms feature is enabled.
*
* @example
* ```vue
*
*
*
* {{ error }}
*
* ```
*/
export function useCopyMarkdown() {
const route = useRoute()
const copied = ref(false)
const loading = ref(false)
const available = ref(false)
const error = ref(null)
const { copy: copyToClipboard } = useClipboard({ legacy: true })
const mdUrl = computed(() => {
const p = route.path !== '/' && route.path.endsWith('/')
? route.path.slice(0, -1)
: route.path
return `${p}.md`
})
// Probe the .md file to detect availability (siteConfig.llms.files enabled at build time)
if (isClient) {
watchEffect(() => {
available.value = false
error.value = null
fetch(mdUrl.value, { method: 'HEAD' })
.then((res) => { available.value = res.ok })
.catch(() => { available.value = false })
})
}
async function copy() {
if (loading.value)
return
error.value = null
loading.value = true
try {
const res = await fetch(mdUrl.value)
if (!res.ok)
throw new Error(`Failed to fetch ${mdUrl.value}: ${res.status}`)
const text = await res.text()
await copyToClipboard(text)
copied.value = true
setTimeout(() => {
copied.value = false
}, 2000)
}
catch (err) {
const msg = err instanceof Error ? err.message : 'Unknown error'
error.value = msg
console.error('[valaxy] Failed to copy markdown:', err)
setTimeout(() => {
error.value = null
}, 3000)
}
finally {
loading.value = false
}
}
return { copy, copied, loading, mdUrl, available, error }
}