<template>
<GnModal
:open="open"
:title="t('share.title')"
@update:open="emit('update:open', $event)"
>
<div class="share-body">
<p class="share-intro">{{ t('share.intro') }}</p>
<div v-if="loading" class="share-loading">
<i class="ph ph-circle-notch share-spin"></i>
{{ t('share.creating') }}
</div>
<p v-else-if="error" class="share-error">
<i class="ph ph-warning-circle"></i>
{{ error }}
</p>
<template v-else-if="url">
<label class="share-label" for="share-url">{{ t('share.linkLabel') }}</label>
<div class="share-link-row">
<input
id="share-url"
class="share-url"
type="text"
readonly
:value="url"
@focus="$event.target.select()"
/>
<GnCopyButton :text="url" :label="t('common.copy')" />
</div>
<p class="share-warning">
<i class="ph ph-warning"></i>
{{ t('share.warning') }}
</p>
<p class="share-hint">{{ t('share.killHint') }}</p>
</template>
<p v-else class="share-hint">{{ t('share.notYet') }}</p>
</div>
<template #actions>
<GnButton variant="secondary" @click="emit('update:open', false)">
{{ t('common.close') }}
</GnButton>
<GnButton
variant="primary"
icon="ph-link"
:loading="loading"
:disabled="!sessionId"
@click="createLink"
>
{{ url ? t('share.regenerate') : t('share.create') }}
</GnButton>
</template>
</GnModal>
</template>
<script setup>
import { ref, watch } from 'vue'
import { GnButton, GnCopyButton, GnModal } from 'gnexus-ui-kit/vue'
import { createChatShare } from '@/api/index.js'
import { useChatStore } from '@/stores/chat.js'
import { t } from '@/i18n/index.js'
const props = defineProps({
open: Boolean,
})
const emit = defineEmits(['update:open'])
const chat = useChatStore()
const sessionId = ref(null)
const url = ref('')
const loading = ref(false)
const error = ref('')
// A new dialog is about the session open right then: a link kept from the
// previous one would name a different chat.
watch(
() => props.open,
open => {
if (!open) return
sessionId.value = chat.currentId
url.value = ''
error.value = ''
loading.value = false
},
{ immediate: true }
)
async function createLink() {
const id = sessionId.value
if (!id || loading.value) return
loading.value = true
error.value = ''
try {
const data = await createChatShare(id)
url.value = data.url
} catch (e) {
// 409 = the session has no saved messages yet, so there is nothing to
// publish; anything else is an unexpected failure.
error.value = String(e?.message || '').includes('409')
? t('share.emptyChat')
: t('share.createFailed')
console.error('[share] create failed', e)
} finally {
loading.value = false
}
}
</script>
<style scoped>
.share-body {
display: flex;
flex-direction: column;
gap: 12px;
min-width: min(420px, calc(100vw - 48px));
}
.share-intro {
margin: 0;
font-size: 13px;
line-height: 1.5;
color: var(--gn-color-text-medium);
}
.share-label {
font-size: 11px;
font-weight: 600;
letter-spacing: 0.04em;
text-transform: uppercase;
color: var(--gn-color-text-light);
}
.share-link-row {
display: flex;
align-items: center;
gap: 8px;
}
.share-url {
flex: 1;
min-width: 0;
padding: 8px 10px;
border: 1px solid var(--gn-border-color-muted);
border-radius: var(--gn-radius-md, 8px);
background: var(--gn-surface-panel-muted, transparent);
color: var(--gn-color-text-dark);
font-family: var(--gn-font-mono, monospace);
font-size: 12px;
}
.share-warning {
display: flex;
gap: 8px;
margin: 0;
padding: 10px 12px;
border: 1px solid color-mix(in srgb, var(--gn-color-warning) 40%, transparent);
border-radius: var(--gn-radius-md, 8px);
background: color-mix(in srgb, var(--gn-color-warning) 8%, transparent);
font-size: 12px;
line-height: 1.5;
color: var(--gn-color-text-medium);
}
.share-warning > i {
flex: none;
margin-top: 1px;
color: var(--gn-color-warning);
}
.share-hint {
margin: 0;
font-size: 12px;
color: var(--gn-color-text-light);
}
.share-loading {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 0;
font-size: 13px;
color: var(--gn-color-text-medium);
}
.share-spin {
animation: share-spin 1s linear infinite;
}
@keyframes share-spin {
to { transform: rotate(360deg); }
}
.share-error {
display: flex;
align-items: center;
gap: 6px;
margin: 0;
font-size: 13px;
color: var(--gn-color-error);
}
</style>