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:
+49
-12
@@ -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>
|
<button type="submit" class="rounded-xl bg-amber-400 px-3 py-2 text-sm font-semibold text-zinc-950">Unlock</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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="font-medium text-zinc-200">Hide input still</span>
|
||||||
<span class="relative inline-flex h-6 w-11 shrink-0 items-center">
|
<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">
|
<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 class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white transition peer-checked:translate-x-5" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</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="font-medium text-zinc-200">Hide output preview</span>
|
||||||
<span class="relative inline-flex h-6 w-11 shrink-0 items-center">
|
<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">
|
<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 class="absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white transition peer-checked:translate-x-5" />
|
||||||
</span>
|
</span>
|
||||||
</label>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -372,7 +383,7 @@
|
|||||||
class="h-full w-full bg-black"
|
class="h-full w-full bg-black"
|
||||||
:class="{ 'opacity-0': awaitingReveal }"
|
:class="{ 'opacity-0': awaitingReveal }"
|
||||||
:controls="!awaitingReveal"
|
:controls="!awaitingReveal"
|
||||||
:autoplay="!awaitingReveal"
|
:autoplay="autoplayEnabled && !awaitingReveal"
|
||||||
loop
|
loop
|
||||||
playsinline
|
playsinline
|
||||||
@play="onPlayed"
|
@play="onPlayed"
|
||||||
@@ -620,6 +631,8 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
const runtimeConfig = useRuntimeConfig()
|
||||||
|
|
||||||
type AspectId = 'auto' | '16:9' | '9:16' | '1:1'
|
type AspectId = 'auto' | '16:9' | '9:16' | '1:1'
|
||||||
|
|
||||||
interface LibraryFolder {
|
interface LibraryFolder {
|
||||||
@@ -745,6 +758,7 @@ const seedInput = ref('random')
|
|||||||
const withSound = ref(true)
|
const withSound = ref(true)
|
||||||
const hideThumbnail = ref(false)
|
const hideThumbnail = ref(false)
|
||||||
const hideInputPreview = ref(false)
|
const hideInputPreview = ref(false)
|
||||||
|
const autoplayEnabled = ref(false)
|
||||||
const JOB_STORE = 'aigen-active-job'
|
const JOB_STORE = 'aigen-active-job'
|
||||||
const busy = ref(false)
|
const busy = ref(false)
|
||||||
const progress = ref(0)
|
const progress = ref(0)
|
||||||
@@ -777,7 +791,8 @@ const comfyBusy = ref(false)
|
|||||||
const userName = ref('')
|
const userName = ref('')
|
||||||
const authEnabled = ref(false)
|
const authEnabled = ref(false)
|
||||||
const authMode = ref('')
|
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 folders = ref<LibraryFolder[]>([])
|
||||||
const clips = ref<LibraryClip[]>([])
|
const clips = ref<LibraryClip[]>([])
|
||||||
const stills = ref<LibraryStill[]>([])
|
const stills = ref<LibraryStill[]>([])
|
||||||
@@ -891,9 +906,19 @@ function clipsIn(id: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function applyThumbnailDefault() {
|
function applyThumbnailDefault() {
|
||||||
|
if (!showPrivacyToggles.value) {
|
||||||
|
hideThumbnail.value = false
|
||||||
|
return
|
||||||
|
}
|
||||||
hideThumbnail.value = currentFolder.value ? !currentFolder.value.thumbnailDefault : false
|
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) {
|
function applyLibrary(data: { folders?: LibraryFolder[]; clips?: LibraryClip[]; stills?: LibraryStill[]; drafts?: RetryDraft[] } | unknown) {
|
||||||
const payload = (data || {}) as { folders?: LibraryFolder[]; clips?: LibraryClip[]; stills?: LibraryStill[]; drafts?: RetryDraft[] }
|
const payload = (data || {}) as { folders?: LibraryFolder[]; clips?: LibraryClip[]; stills?: LibraryStill[]; drafts?: RetryDraft[] }
|
||||||
folders.value = payload.folders || []
|
folders.value = payload.folders || []
|
||||||
@@ -927,7 +952,9 @@ onMounted(async () => {
|
|||||||
try {
|
try {
|
||||||
hideInputPreview.value = localStorage.getItem('aigen-hide-input-preview') === 'true'
|
hideInputPreview.value = localStorage.getItem('aigen-hide-input-preview') === 'true'
|
||||||
withSound.value = localStorage.getItem('aigen-generate-sound') !== 'false'
|
withSound.value = localStorage.getItem('aigen-generate-sound') !== 'false'
|
||||||
|
autoplayEnabled.value = localStorage.getItem('aigen-autoplay') === 'true'
|
||||||
} catch { /* ignore */ }
|
} catch { /* ignore */ }
|
||||||
|
applyInstancePrivacy()
|
||||||
window.addEventListener('pointerdown', onSitePointerDown, true)
|
window.addEventListener('pointerdown', onSitePointerDown, true)
|
||||||
const [health, me] = await Promise.all([
|
const [health, me] = await Promise.all([
|
||||||
$fetch<{ comfy?: { ok?: boolean; busy?: boolean } }>('/api/health').catch(() => ({ comfy: { ok: false, busy: false } })),
|
$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 || ''
|
userName.value = me.user?.name || me.user?.email || ''
|
||||||
authEnabled.value = Boolean(me.authEnabled)
|
authEnabled.value = Boolean(me.authEnabled)
|
||||||
authMode.value = me.authMode || ''
|
authMode.value = me.authMode || ''
|
||||||
instanceName.value = me.instanceName || 'AIGen'
|
instanceName.value = me.instanceName || instanceName.value || 'AIGen'
|
||||||
|
applyInstancePrivacy()
|
||||||
healthTimer = setInterval(() => {
|
healthTimer = setInterval(() => {
|
||||||
void pollComfyHealth()
|
void pollComfyHealth()
|
||||||
}, 5000)
|
}, 5000)
|
||||||
@@ -970,6 +998,15 @@ watch(withSound, (value) => {
|
|||||||
} catch { /* ignore */ }
|
} 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) => {
|
watch(duration, (value) => {
|
||||||
if (!durationScrubbing.value) durationDraft.value = formatDuration(value)
|
if (!durationScrubbing.value) durationDraft.value = formatDuration(value)
|
||||||
})
|
})
|
||||||
@@ -1113,7 +1150,7 @@ function readFile(next: File) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function onPickedImage(next: File, hidden = false) {
|
function onPickedImage(next: File, hidden = false) {
|
||||||
if (hidden) hideInputPreview.value = true
|
if (hidden && showPrivacyToggles.value) hideInputPreview.value = true
|
||||||
readFile(next)
|
readFile(next)
|
||||||
pickerOpen.value = false
|
pickerOpen.value = false
|
||||||
}
|
}
|
||||||
@@ -1239,7 +1276,7 @@ function rerun(item: LibraryClip) {
|
|||||||
clipName.value = item.name || ''
|
clipName.value = item.name || ''
|
||||||
seedInput.value = String(item.seed)
|
seedInput.value = String(item.seed)
|
||||||
restoreAdvanced(item)
|
restoreAdvanced(item)
|
||||||
hideThumbnail.value = item.hideThumbnail
|
hideThumbnail.value = showPrivacyToggles.value && item.hideThumbnail
|
||||||
folderId.value = item.folderId
|
folderId.value = item.folderId
|
||||||
toast('Settings restored. Choose the same still if it is not already loaded.')
|
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 || ''
|
clipName.value = draft.name || ''
|
||||||
seedInput.value = String(draft.seed)
|
seedInput.value = String(draft.seed)
|
||||||
restoreAdvanced(draft)
|
restoreAdvanced(draft)
|
||||||
hideThumbnail.value = draft.hideThumbnail
|
hideThumbnail.value = showPrivacyToggles.value && draft.hideThumbnail
|
||||||
folderId.value = draft.folderId
|
folderId.value = draft.folderId
|
||||||
withSound.value = draft.sound !== false
|
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))
|
duration.value = clampDuration(Number(draft.duration))
|
||||||
durationDraft.value = formatDuration(duration.value)
|
durationDraft.value = formatDuration(duration.value)
|
||||||
extensionQueue.value = (draft.extensions || []).slice(0, MAX_EXTENSIONS).map(item => ({
|
extensionQueue.value = (draft.extensions || []).slice(0, MAX_EXTENSIONS).map(item => ({
|
||||||
@@ -1578,8 +1615,8 @@ async function generate() {
|
|||||||
body.append('scheduler', scheduler.value)
|
body.append('scheduler', scheduler.value)
|
||||||
body.append('seed', seedInput.value || 'random')
|
body.append('seed', seedInput.value || 'random')
|
||||||
body.append('folderId', folderId.value)
|
body.append('folderId', folderId.value)
|
||||||
body.append('hideThumbnail', String(hideThumbnail.value))
|
body.append('hideThumbnail', String(showPrivacyToggles.value && hideThumbnail.value))
|
||||||
body.append('hideInput', String(hideInputPreview.value))
|
body.append('hideInput', String(showPrivacyToggles.value && hideInputPreview.value))
|
||||||
body.append('sound', String(withSound.value))
|
body.append('sound', String(withSound.value))
|
||||||
body.append('name', clipName.value.trim())
|
body.append('name', clipName.value.trim())
|
||||||
if (activeDraftId) body.append('draftId', activeDraftId)
|
if (activeDraftId) body.append('draftId', activeDraftId)
|
||||||
|
|||||||
Reference in New Issue
Block a user