Slim the phone stage to icons, chevrons, and a More sheet.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+116
-61
@@ -47,35 +47,46 @@
|
|||||||
</div>
|
</div>
|
||||||
<div v-else class="m-chips"><span class="m-chip on">YuE2</span></div>
|
<div v-else class="m-chips"><span class="m-chip on">YuE2</span></div>
|
||||||
|
|
||||||
<div class="m-stage">
|
<div
|
||||||
|
class="m-stage"
|
||||||
|
@touchstart.passive="onStageTouchStart"
|
||||||
|
@touchend.passive="onStageTouchEnd"
|
||||||
|
>
|
||||||
<div class="m-stage-chrome" aria-label="Stage controls">
|
<div class="m-stage-chrome" aria-label="Stage controls">
|
||||||
<div class="m-stage-chrome-top">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="m-library"
|
class="m-icon-btn m-library"
|
||||||
:aria-expanded="ctx.libraryOpen ? 'true' : 'false'"
|
:aria-expanded="ctx.libraryOpen ? 'true' : 'false'"
|
||||||
|
aria-label="Library"
|
||||||
@click="ctx.openLibrary()"
|
@click="ctx.openLibrary()"
|
||||||
>Library<span v-if="ctx.familyStillCount" class="m-library-count">{{ ctx.familyStillCount }}</span></button>
|
>
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true">
|
||||||
|
<rect x="3" y="4" width="7" height="7" rx="1.5" />
|
||||||
|
<rect x="14" y="4" width="7" height="7" rx="1.5" />
|
||||||
|
<rect x="3" y="13" width="7" height="7" rx="1.5" />
|
||||||
|
<rect x="14" y="13" width="7" height="7" rx="1.5" />
|
||||||
|
</svg>
|
||||||
|
<span v-if="ctx.familyStillCount" class="m-library-badge">{{ ctx.familyStillCount > 99 ? '99+' : ctx.familyStillCount }}</span>
|
||||||
|
</button>
|
||||||
|
<div class="m-stage-chrome-end">
|
||||||
|
<button
|
||||||
|
v-if="ctx.selected && ctx.selected.kind!=='track'"
|
||||||
|
type="button"
|
||||||
|
class="m-icon-btn"
|
||||||
|
aria-label="More"
|
||||||
|
:aria-expanded="sheet==='actions' ? 'true' : 'false'"
|
||||||
|
@click.stop="toggleSheet('actions')"
|
||||||
|
>•••</button>
|
||||||
<button
|
<button
|
||||||
v-if="ctx.selected"
|
v-if="ctx.selected"
|
||||||
type="button"
|
type="button"
|
||||||
class="m-eye"
|
class="m-icon-btn m-eye"
|
||||||
:aria-label="ctx.eyeOpen ? 'Hide output' : 'Show output'"
|
:aria-label="ctx.eyeOpen ? 'Hide output' : 'Show output'"
|
||||||
@click.stop="ctx.toggleReveal()"
|
@click.stop="ctx.toggleReveal()"
|
||||||
>
|
>
|
||||||
<img :src="ctx.eyeOpen ? '/icons/display/eye.svg' : '/icons/display/eye-off.svg'" alt="" width="20" height="20">
|
<img :src="ctx.eyeOpen ? '/icons/display/eye.svg' : '/icons/display/eye-off.svg'" alt="" width="20" height="20">
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
v-if="ctx.selected && ctx.selected.kind!=='track'"
|
|
||||||
class="m-stage-actions"
|
|
||||||
aria-label="Selected still actions"
|
|
||||||
>
|
|
||||||
<button type="button" @click.stop="ctx.applyUseAsSource">Use as photo</button>
|
|
||||||
<button type="button" @click.stop="ctx.copySelectedPrompt">Copy prompt</button>
|
|
||||||
<button type="button" @click.stop="ctx.loadSelectedJob">Load</button>
|
|
||||||
<button type="button" class="m-danger" @click.stop="ctx.deleteOutput">Delete</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
v-if="(ctx.family || []).length > 1"
|
v-if="(ctx.family || []).length > 1"
|
||||||
@@ -92,7 +103,7 @@
|
|||||||
@click.stop="ctx.stepFamily(1)"
|
@click.stop="ctx.stepFamily(1)"
|
||||||
>›</button>
|
>›</button>
|
||||||
<template v-if="ctx.selected && ctx.isHidden(ctx.selected)">
|
<template v-if="ctx.selected && ctx.isHidden(ctx.selected)">
|
||||||
<p class="m-muted">{{ ctx.selected.kind==='track' ? 'Track loaded · preview hidden' : 'Output hidden' }}</p>
|
<p class="m-hidden-note">{{ ctx.selected.kind==='track' ? 'Track hidden' : 'Output hidden' }}</p>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="ctx.selected?.kind==='track'">
|
<template v-else-if="ctx.selected?.kind==='track'">
|
||||||
<div class="m-audio">
|
<div class="m-audio">
|
||||||
@@ -135,6 +146,16 @@
|
|||||||
|
|
||||||
<!-- Sheets -->
|
<!-- Sheets -->
|
||||||
<div v-if="sheet" class="m-sheet-backdrop" @click="sheet=''" />
|
<div v-if="sheet" class="m-sheet-backdrop" @click="sheet=''" />
|
||||||
|
<aside v-if="sheet==='actions'" class="m-sheet m-sheet-actions" role="dialog" aria-label="Still actions">
|
||||||
|
<button type="button" class="m-handle" aria-label="Close" @click="sheet=''" />
|
||||||
|
<div class="m-sheet-head"><strong>Actions</strong><button type="button" @click="sheet=''">Done</button></div>
|
||||||
|
<div class="m-sheet-body m-actions-list">
|
||||||
|
<button type="button" @click="runAction('use')">Use as photo</button>
|
||||||
|
<button type="button" @click="runAction('copy')">Copy prompt</button>
|
||||||
|
<button type="button" @click="runAction('load')">Load</button>
|
||||||
|
<button type="button" class="m-danger" @click="runAction('delete')">Delete</button>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
<aside v-if="sheet==='slots'" class="m-sheet" role="dialog" aria-label="Photos">
|
<aside v-if="sheet==='slots'" class="m-sheet" role="dialog" aria-label="Photos">
|
||||||
<button type="button" class="m-handle" aria-label="Close" @click="sheet=''" />
|
<button type="button" class="m-handle" aria-label="Close" @click="sheet=''" />
|
||||||
<div class="m-sheet-head"><strong>Photos</strong><button type="button" @click="sheet=''">Done</button></div>
|
<div class="m-sheet-head"><strong>Photos</strong><button type="button" @click="sheet=''">Done</button></div>
|
||||||
@@ -331,6 +352,7 @@ export type Studio2MobileCtx = Record<string, any>
|
|||||||
|
|
||||||
const props = defineProps<{ ctx: Studio2MobileCtx }>()
|
const props = defineProps<{ ctx: Studio2MobileCtx }>()
|
||||||
const sheet = ref('')
|
const sheet = ref('')
|
||||||
|
const touchStart = ref<{ x: number; y: number } | null>(null)
|
||||||
|
|
||||||
function toggleSheet(id: string) {
|
function toggleSheet(id: string) {
|
||||||
sheet.value = sheet.value === id ? '' : id
|
sheet.value = sheet.value === id ? '' : id
|
||||||
@@ -338,6 +360,28 @@ function toggleSheet(id: string) {
|
|||||||
function closeMenus() {
|
function closeMenus() {
|
||||||
sheet.value = ''
|
sheet.value = ''
|
||||||
}
|
}
|
||||||
|
function runAction(kind: 'use' | 'copy' | 'load' | 'delete') {
|
||||||
|
sheet.value = ''
|
||||||
|
if (kind === 'use') props.ctx.applyUseAsSource?.()
|
||||||
|
else if (kind === 'copy') props.ctx.copySelectedPrompt?.()
|
||||||
|
else if (kind === 'load') props.ctx.loadSelectedJob?.()
|
||||||
|
else if (kind === 'delete') props.ctx.deleteOutput?.()
|
||||||
|
}
|
||||||
|
function onStageTouchStart(e: TouchEvent) {
|
||||||
|
const t = e.changedTouches[0]
|
||||||
|
if (!t) return
|
||||||
|
touchStart.value = { x: t.clientX, y: t.clientY }
|
||||||
|
}
|
||||||
|
function onStageTouchEnd(e: TouchEvent) {
|
||||||
|
const start = touchStart.value
|
||||||
|
touchStart.value = null
|
||||||
|
const t = e.changedTouches[0]
|
||||||
|
if (!start || !t || (props.ctx.family || []).length < 2) return
|
||||||
|
const dx = t.clientX - start.x
|
||||||
|
const dy = t.clientY - start.y
|
||||||
|
if (Math.abs(dx) < 48 || Math.abs(dx) < Math.abs(dy) * 1.4) return
|
||||||
|
props.ctx.stepFamily?.(dx < 0 ? 1 : -1)
|
||||||
|
}
|
||||||
watch(() => props.ctx.form.mode, () => { sheet.value = '' })
|
watch(() => props.ctx.form.mode, () => { sheet.value = '' })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -447,76 +491,67 @@ watch(() => props.ctx.form.mode, () => { sheet.value = '' })
|
|||||||
.m-audio { width: 100%; max-width: 100%; padding: 12px; }
|
.m-audio { width: 100%; max-width: 100%; padding: 12px; }
|
||||||
.m-stage-chrome {
|
.m-stage-chrome {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 8px;
|
top: calc(8px + env(safe-area-inset-top, 0px));
|
||||||
right: 8px;
|
right: 8px;
|
||||||
left: 8px;
|
left: 8px;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
.m-stage-chrome-top {
|
.m-stage-chrome > *,
|
||||||
display: flex;
|
.m-stage-chrome-end {
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
.m-stage-chrome-top > *,
|
|
||||||
.m-stage-actions {
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
.m-library {
|
.m-stage-chrome-end {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
min-height: 48px;
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 8px;
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 8px;
|
|
||||||
background: #000a;
|
|
||||||
padding: 0 14px;
|
|
||||||
color: var(--text);
|
|
||||||
font: inherit;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
.m-library-count {
|
.m-icon-btn {
|
||||||
color: var(--muted);
|
position: relative;
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
.m-eye {
|
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 48px;
|
width: 44px;
|
||||||
height: 48px;
|
height: 44px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: #000a;
|
background: #000a;
|
||||||
}
|
|
||||||
.m-stage-actions {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 6px;
|
|
||||||
max-width: calc(100% - 56px);
|
|
||||||
}
|
|
||||||
.m-stage-actions button {
|
|
||||||
min-height: 44px;
|
|
||||||
border: 1px solid var(--line);
|
|
||||||
border-radius: 8px;
|
|
||||||
background: #000a;
|
|
||||||
color: var(--text);
|
color: var(--text);
|
||||||
padding: 0 10px;
|
|
||||||
font: inherit;
|
font: inherit;
|
||||||
font-size: 12px;
|
font-size: 16px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.m-library-badge {
|
||||||
|
position: absolute;
|
||||||
|
top: -4px;
|
||||||
|
right: -4px;
|
||||||
|
min-width: 18px;
|
||||||
|
border-radius: 999px;
|
||||||
|
background: var(--amber);
|
||||||
|
color: var(--amber-on);
|
||||||
|
font-size: 10px;
|
||||||
|
font-weight: 700;
|
||||||
|
line-height: 16px;
|
||||||
|
padding: 0 4px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.m-hidden-note {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--muted);
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
.m-stage-actions .m-danger { color: #fca5a5; }
|
|
||||||
.m-nav {
|
.m-nav {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
z-index: 4;
|
z-index: 4;
|
||||||
display: grid;
|
display: grid;
|
||||||
width: 48px;
|
width: 44px;
|
||||||
height: 48px;
|
height: 44px;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
border: 1px solid var(--line);
|
border: 1px solid var(--line);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -524,11 +559,31 @@ watch(() => props.ctx.form.mode, () => { sheet.value = '' })
|
|||||||
color: var(--text);
|
color: var(--text);
|
||||||
font-size: 28px;
|
font-size: 28px;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
|
opacity: 0.7;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
}
|
}
|
||||||
.m-nav-prev { left: 8px; }
|
.m-nav-prev { left: 8px; }
|
||||||
.m-nav-next { right: 8px; }
|
.m-nav-next { right: 8px; }
|
||||||
.m-photos-head { margin-bottom: 8px; }
|
.m-photos-head { margin-bottom: 8px; }
|
||||||
|
.m-sheet-actions {
|
||||||
|
max-height: min(50vh, 100%);
|
||||||
|
}
|
||||||
|
.m-actions-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
.m-actions-list button {
|
||||||
|
min-height: 48px;
|
||||||
|
border: 1px solid var(--line);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #1a1a1d;
|
||||||
|
color: var(--text);
|
||||||
|
font: inherit;
|
||||||
|
text-align: left;
|
||||||
|
padding: 0 14px;
|
||||||
|
}
|
||||||
|
.m-actions-list .m-danger { color: #fca5a5; }
|
||||||
.m-dock {
|
.m-dock {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr) 120px;
|
grid-template-columns: repeat(4, 1fr) 120px;
|
||||||
|
|||||||
Reference in New Issue
Block a user