feat(ui): add site context menu
Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -21,7 +21,6 @@ const campaigns = ref<CampaignRow[]>([])
|
||||
const drafts = ref<DraftSession[]>([])
|
||||
const loading = ref(true)
|
||||
const error = ref('')
|
||||
const editingCampaignId = ref<string | null>(null)
|
||||
const renamingCampaignId = ref<string | null>(null)
|
||||
const newName = ref('')
|
||||
|
||||
@@ -44,19 +43,18 @@ function draftSummary(draft: DraftSession) {
|
||||
|| 'Continue your conversation with the coauthor.'
|
||||
}
|
||||
|
||||
function toggleEdit(campaign: CampaignRow) {
|
||||
if (editingCampaignId.value === campaign.id) {
|
||||
editingCampaignId.value = null
|
||||
} else {
|
||||
editingCampaignId.value = campaign.id
|
||||
}
|
||||
}
|
||||
|
||||
function openRename(campaign: CampaignRow) {
|
||||
renamingCampaignId.value = campaign.id
|
||||
newName.value = campaign.title
|
||||
}
|
||||
|
||||
function handleCampaignAction(payload: { action: 'rename' | 'delete'; campaignId: string }) {
|
||||
const campaign = campaigns.value.find(item => item.id === payload.campaignId)
|
||||
if (!campaign) return
|
||||
if (payload.action === 'rename') openRename(campaign)
|
||||
else void requestDelete(campaign)
|
||||
}
|
||||
|
||||
async function confirmRename() {
|
||||
if (!renamingCampaignId.value || !newName.value.trim()) return
|
||||
try {
|
||||
@@ -92,7 +90,7 @@ onMounted(async () => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppShell section="COMMAND DECK">
|
||||
<AppShell section="COMMAND DECK" @campaign-action="handleCampaignAction">
|
||||
<div class="dash-wrap noise">
|
||||
<section class="dash-head">
|
||||
<div><p class="kicker">WELCOME BACK, {{ displayName.toUpperCase() }}</p><h1>YOUR UNIVERSES<span>.</span></h1><p>Every world remembers where you left it.</p></div>
|
||||
@@ -107,17 +105,16 @@ onMounted(async () => {
|
||||
<section class="world-grid">
|
||||
<p v-if="loading" class="load-state">RECEIVING PRIVATE CAMPAIGNS…</p>
|
||||
<p v-else-if="error" class="load-state error">{{ error }} <NuxtLink to="/auth/sign-in">SIGN IN AGAIN</NuxtLink></p>
|
||||
<NuxtLink v-for="campaign in visibleCampaigns" :key="campaign.id" :to="`/campaign/${campaign.id}`" class="world-card active-world">
|
||||
<NuxtLink
|
||||
v-for="campaign in visibleCampaigns"
|
||||
:key="campaign.id"
|
||||
:to="`/campaign/${campaign.id}`"
|
||||
class="world-card active-world"
|
||||
:data-context-campaign="campaign.id"
|
||||
:data-context-label="campaign.title"
|
||||
>
|
||||
<div class="world-art"><div class="eclipse" /><span>{{ campaign.status.toUpperCase() }} CAMPAIGN</span></div>
|
||||
<div class="world-info"><small>PRIVATE MULTIPLAYER</small><h2>{{ campaign.title }}</h2><p>{{ campaign.current_scene }}</p><div><b>CONTINUE STORY</b><span>{{ new Date(campaign.updated_at).toLocaleDateString() }}</span></div></div>
|
||||
<div class="card-actions" :class="{active: editingCampaignId === campaign.id}">
|
||||
<template v-if="editingCampaignId === campaign.id">
|
||||
<button @click.stop="openRename(campaign)" title="Rename this universe">✎</button>
|
||||
<button @click.stop="requestDelete(campaign)" title="Delete this universe">✕</button>
|
||||
<button @click.stop="toggleEdit(campaign)" class="ghost-action" title="Done editing">DONE</button>
|
||||
</template>
|
||||
<button v-else @click.stop="toggleEdit(campaign)" title="Edit this universe">EDIT</button>
|
||||
</div>
|
||||
</NuxtLink>
|
||||
<NuxtLink v-for="draft in visibleDrafts" :key="draft.id" :to="`/worlds/new?session=${draft.id}`" class="world-card active-world draft-world">
|
||||
<div class="world-art"><div class="eclipse" /><span>{{ draft.status.toUpperCase() }} DRAFT</span></div>
|
||||
@@ -183,12 +180,6 @@ onMounted(async () => {
|
||||
.system-strip{margin-top:32px;padding:18px 0;border-top:1px solid var(--line);display:flex;justify-content:space-between;font:500 8px var(--mono);letter-spacing:.12em;color:var(--muted)}
|
||||
.system-strip i{display:inline-block;width:6px;height:6px;background:var(--acid);border-radius:50%;margin-right:8px}
|
||||
.system-strip b{color:var(--acid)}
|
||||
.card-actions{position:absolute;top:12px;right:12px;display:flex;gap:6px;z-index:2;opacity:0;pointer-events:none;transition:opacity .2s ease}
|
||||
.card-actions.active{opacity:1;pointer-events:auto}
|
||||
.card-actions button{height:34px;border:1px solid var(--line);background:rgba(10,10,10,.75);color:var(--muted);font:600 8px var(--mono);letter-spacing:.1em;line-height:1;display:grid;place-items:center;padding:0 12px;transition:all .2s ease;cursor:pointer}
|
||||
.card-actions button.icon-btn{width:34px;font-size:14px}
|
||||
.card-actions button:hover,.card-actions button.active-item{border-color:var(--acid);color:var(--acid);background:rgba(217,247,95,.08)}
|
||||
.card-actions button.ghost-action{border-color:transparent;color:var(--muted)}
|
||||
.rename-dialog{position:fixed;inset:0;margin:auto;z-index:1000;width:min(440px,calc(100vw - 40px));padding:0;border:1px solid var(--line);background:#0a0a0a;color:var(--ink);box-shadow:0 60px 120px rgba(0,0,0,.7);font-family:inherit;display:grid}
|
||||
.rename-dialog::backdrop{background:rgba(0,0,0,.55)}
|
||||
.rename-panel{padding:32px;display:grid;gap:18px}
|
||||
|
||||
Reference in New Issue
Block a user