Hide privacy toggles on AIGen and default autoplay off.

xAIGen keeps hide-input and hide-output; both instances get an autoplay switch that starts off so finished clips wait for play.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Towsty
2026-08-26 00:07:31 -05:00
co-authored by Cursor
parent f5c709924b
commit 046cc51a1b
+49 -12
View File
@@ -146,7 +146,7 @@
<button type="submit" class="rounded-xl bg-amber-400 px-3 py-2 text-sm font-semibold text-zinc-950">Unlock</button>
</div>
</form>
<label class="flex cursor-pointer items-center justify-between gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3 text-sm">
<label v-if="showPrivacyToggles" class="flex cursor-pointer items-center justify-between gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3 text-sm">
<span class="font-medium text-zinc-200">Hide input still</span>
<span class="relative inline-flex h-6 w-11 shrink-0 items-center">
<input v-model="hideInputPreview" type="checkbox" class="peer sr-only" role="switch">
@@ -154,7 +154,7 @@
<span class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white transition peer-checked:translate-x-5" />
</span>
</label>
<label class="flex cursor-pointer items-center justify-between gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3 text-sm">
<label v-if="showPrivacyToggles" class="flex cursor-pointer items-center justify-between gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3 text-sm">
<span class="font-medium text-zinc-200">Hide output preview</span>
<span class="relative inline-flex h-6 w-11 shrink-0 items-center">
<input v-model="hideThumbnail" type="checkbox" class="peer sr-only" role="switch">
@@ -173,7 +173,18 @@
<span class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white transition peer-checked:translate-x-5" />
</span>
</label>
<p class="text-xs text-zinc-500">Output preview hides library thumbs and autoplay. Click the player to reveal; click anywhere else on the page to hide it again. Folder default applies to output preview when you pick a new still.</p>
<label class="flex cursor-pointer items-center justify-between gap-3 rounded-2xl border border-white/10 bg-zinc-950/40 px-4 py-3 text-sm">
<span>
<span class="block font-medium text-zinc-200">Autoplay</span>
<span class="mt-0.5 block text-xs text-zinc-500">Off leaves finished clips paused until you press play.</span>
</span>
<span class="relative inline-flex h-6 w-11 shrink-0 items-center">
<input v-model="autoplayEnabled" type="checkbox" class="peer sr-only" role="switch">
<span class="absolute inset-0 rounded-full bg-zinc-700 transition peer-checked:bg-amber-400" />
<span class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white transition peer-checked:translate-x-5" />
</span>
</label>
<p v-if="showPrivacyToggles" class="text-xs text-zinc-500">Output preview hides library thumbs and autoplay. Click the player to reveal; click anywhere else on the page to hide it again. Folder default applies to output preview when you pick a new still.</p>
</div>
</div>
@@ -372,7 +383,7 @@
class="h-full w-full bg-black"
:class="{ 'opacity-0': awaitingReveal }"
:controls="!awaitingReveal"
:autoplay="!awaitingReveal"
:autoplay="autoplayEnabled && !awaitingReveal"
loop
playsinline
@play="onPlayed"
@@ -620,6 +631,8 @@
</template>
<script setup lang="ts">
const runtimeConfig = useRuntimeConfig()
type AspectId = 'auto' | '16:9' | '9:16' | '1:1'
interface LibraryFolder {
@@ -745,6 +758,7 @@ const seedInput = ref('random')
const withSound = ref(true)
const hideThumbnail = ref(false)
const hideInputPreview = ref(false)
const autoplayEnabled = ref(false)
const JOB_STORE = 'aigen-active-job'
const busy = ref(false)
const progress = ref(0)
@@ -777,7 +791,8 @@ const comfyBusy = ref(false)
const userName = ref('')
const authEnabled = ref(false)
const authMode = ref('')
const instanceName = ref('AIGen')
const instanceName = ref(String(runtimeConfig.public.instanceName || 'AIGen'))
const showPrivacyToggles = computed(() => /xaigen/i.test(String(instanceName.value)))
const folders = ref<LibraryFolder[]>([])
const clips = ref<LibraryClip[]>([])
const stills = ref<LibraryStill[]>([])
@@ -891,9 +906,19 @@ function clipsIn(id: string) {
}
function applyThumbnailDefault() {
if (!showPrivacyToggles.value) {
hideThumbnail.value = false
return
}
hideThumbnail.value = currentFolder.value ? !currentFolder.value.thumbnailDefault : false
}
function applyInstancePrivacy() {
if (showPrivacyToggles.value) return
hideInputPreview.value = false
hideThumbnail.value = false
}
function applyLibrary(data: { folders?: LibraryFolder[]; clips?: LibraryClip[]; stills?: LibraryStill[]; drafts?: RetryDraft[] } | unknown) {
const payload = (data || {}) as { folders?: LibraryFolder[]; clips?: LibraryClip[]; stills?: LibraryStill[]; drafts?: RetryDraft[] }
folders.value = payload.folders || []
@@ -927,7 +952,9 @@ onMounted(async () => {
try {
hideInputPreview.value = localStorage.getItem('aigen-hide-input-preview') === 'true'
withSound.value = localStorage.getItem('aigen-generate-sound') !== 'false'
autoplayEnabled.value = localStorage.getItem('aigen-autoplay') === 'true'
} catch { /* ignore */ }
applyInstancePrivacy()
window.addEventListener('pointerdown', onSitePointerDown, true)
const [health, me] = await Promise.all([
$fetch<{ comfy?: { ok?: boolean; busy?: boolean } }>('/api/health').catch(() => ({ comfy: { ok: false, busy: false } })),
@@ -938,7 +965,8 @@ onMounted(async () => {
userName.value = me.user?.name || me.user?.email || ''
authEnabled.value = Boolean(me.authEnabled)
authMode.value = me.authMode || ''
instanceName.value = me.instanceName || 'AIGen'
instanceName.value = me.instanceName || instanceName.value || 'AIGen'
applyInstancePrivacy()
healthTimer = setInterval(() => {
void pollComfyHealth()
}, 5000)
@@ -970,6 +998,15 @@ watch(withSound, (value) => {
} catch { /* ignore */ }
})
watch(autoplayEnabled, (value) => {
try {
localStorage.setItem('aigen-autoplay', String(value))
} catch { /* ignore */ }
if (value && videoUrl.value && !awaitingReveal.value) {
player.value?.play().catch(() => null)
}
})
watch(duration, (value) => {
if (!durationScrubbing.value) durationDraft.value = formatDuration(value)
})
@@ -1113,7 +1150,7 @@ function readFile(next: File) {
}
function onPickedImage(next: File, hidden = false) {
if (hidden) hideInputPreview.value = true
if (hidden && showPrivacyToggles.value) hideInputPreview.value = true
readFile(next)
pickerOpen.value = false
}
@@ -1239,7 +1276,7 @@ function rerun(item: LibraryClip) {
clipName.value = item.name || ''
seedInput.value = String(item.seed)
restoreAdvanced(item)
hideThumbnail.value = item.hideThumbnail
hideThumbnail.value = showPrivacyToggles.value && item.hideThumbnail
folderId.value = item.folderId
toast('Settings restored. Choose the same still if it is not already loaded.')
}
@@ -1250,10 +1287,10 @@ async function loadDraft(draft: RetryDraft) {
clipName.value = draft.name || ''
seedInput.value = String(draft.seed)
restoreAdvanced(draft)
hideThumbnail.value = draft.hideThumbnail
hideThumbnail.value = showPrivacyToggles.value && draft.hideThumbnail
folderId.value = draft.folderId
withSound.value = draft.sound !== false
if (draft.hideInput) hideInputPreview.value = true
if (draft.hideInput && showPrivacyToggles.value) hideInputPreview.value = true
duration.value = clampDuration(Number(draft.duration))
durationDraft.value = formatDuration(duration.value)
extensionQueue.value = (draft.extensions || []).slice(0, MAX_EXTENSIONS).map(item => ({
@@ -1578,8 +1615,8 @@ async function generate() {
body.append('scheduler', scheduler.value)
body.append('seed', seedInput.value || 'random')
body.append('folderId', folderId.value)
body.append('hideThumbnail', String(hideThumbnail.value))
body.append('hideInput', String(hideInputPreview.value))
body.append('hideThumbnail', String(showPrivacyToggles.value && hideThumbnail.value))
body.append('hideInput', String(showPrivacyToggles.value && hideInputPreview.value))
body.append('sound', String(withSound.value))
body.append('name', clipName.value.trim())
if (activeDraftId) body.append('draftId', activeDraftId)