feat(alpha): complete closed-alpha readiness stage
Some checks failed
CI / validate (push) Has been cancelled

Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
2026-09-03 20:40:40 +05:00
parent b5a9e2d61f
commit e67167648b
8 changed files with 384 additions and 40 deletions

View File

@@ -0,0 +1,78 @@
<script setup lang="ts">
interface OnboardingCampaign {
id: string
}
interface OnboardingDraft {
id: string
}
const props = defineProps<{
userId: string
campaigns: OnboardingCampaign[]
drafts: OnboardingDraft[]
}>()
const visible = ref(false)
const storageKey = computed(() => `dng-alpha-onboarding:${props.userId}`)
const completed = computed(() => props.campaigns.length ? 2 : props.drafts.length ? 1 : 0)
const nextRoute = computed(() => {
if (props.campaigns[0]) return `/campaign/${props.campaigns[0].id}`
if (props.drafts[0]) return `/worlds/new?session=${props.drafts[0].id}`
return '/worlds/new'
})
const nextLabel = computed(() => props.campaigns.length
? 'OPEN YOUR CAMPAIGN'
: props.drafts.length ? 'CONTINUE YOUR WORLD' : 'CREATE YOUR FIRST WORLD')
function restoreVisibility() {
try {
visible.value = localStorage.getItem(storageKey.value) !== 'dismissed'
} catch {
visible.value = true
}
}
function dismiss() {
try {
localStorage.setItem(storageKey.value, 'dismissed')
} catch {
// Storage may be disabled in a private browsing context. The guide can
// still be dismissed for the current page lifetime.
}
visible.value = false
}
onMounted(restoreVisibility)
watch(() => props.userId, restoreVisibility)
</script>
<template>
<section v-if="visible" class="onboarding" aria-labelledby="onboarding-title">
<header>
<div>
<small>ALPHA FIELD GUIDE · {{ completed + 1 }} / 3</small>
<h2 id="onboarding-title">START YOUR FIRST STORY.</h2>
<p>Three short steps take you from a single idea to a shared, persistent campaign.</p>
</div>
<button type="button" aria-label="Dismiss getting started guide" @click="dismiss">×</button>
</header>
<div class="progress" aria-hidden="true"><i :style="{ width: `${Math.max(8, completed / 3 * 100)}%` }" /></div>
<ol>
<li :class="{ done: completed >= 1, active: completed === 0 }">
<b>01</b><span><strong>DESCRIBE A UNIVERSE</strong><small>Answer the coauthors focused questions.</small></span>
</li>
<li :class="{ done: completed >= 2, active: completed === 1 }">
<b>02</b><span><strong>CONFIRM THE STARTER</strong><small>Review the world, companions, and opening scene.</small></span>
</li>
<li :class="{ active: completed >= 2 }">
<b>03</b><span><strong>ASSEMBLE &amp; ACT</strong><small>Invite friends, write an action, then mark it ready.</small></span>
</li>
</ol>
<NuxtLink :to="nextRoute">{{ nextLabel }} <span></span></NuxtLink>
</section>
</template>
<style scoped>
.onboarding{margin-top:34px;border:1px solid #414139;background:linear-gradient(120deg,rgba(217,247,95,.08),transparent 56%),#0d0d0c}.onboarding>header{display:flex;justify-content:space-between;gap:24px;padding:25px 26px 20px}.onboarding header small{font:600 7px var(--mono);letter-spacing:.17em;color:var(--acid)}.onboarding h2{margin:10px 0 7px;font:600 clamp(20px,2.8vw,34px)/1.05 var(--display);letter-spacing:-.045em}.onboarding header p{margin:0;max-width:620px;color:var(--muted);font-size:11px;line-height:1.6}.onboarding header button{align-self:start;width:34px;height:34px;border:1px solid var(--line);background:#0b0b0a;color:var(--muted);font-size:20px}.progress{height:2px;background:#242422}.progress i{display:block;height:100%;min-width:8px;background:var(--acid);transition:width .3s ease}.onboarding ol{list-style:none;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));margin:0;padding:0}.onboarding li{display:grid;grid-template-columns:28px minmax(0,1fr);gap:10px;padding:20px 24px;border-right:1px solid var(--line);opacity:.46}.onboarding li:last-child{border-right:0}.onboarding li.active,.onboarding li.done{opacity:1}.onboarding li>b{font:600 8px var(--mono);color:var(--muted)}.onboarding li.done>b,.onboarding li.active>b{color:var(--acid)}.onboarding li.done>b::after{content:'✓';display:block;margin-top:6px}.onboarding li span{display:grid;gap:7px}.onboarding li strong{font:600 8px var(--mono);letter-spacing:.09em}.onboarding li small{color:var(--muted);font-size:9px;line-height:1.5}.onboarding>a{display:flex;align-items:center;justify-content:space-between;padding:17px 25px;border-top:1px solid var(--line);background:var(--acid);color:#090909;text-decoration:none;font:700 8px var(--mono);letter-spacing:.12em}.onboarding>a span{font-size:15px}@media(max-width:760px){.onboarding ol{grid-template-columns:1fr}.onboarding li{border-right:0;border-bottom:1px solid var(--line)}.onboarding li:last-child{border-bottom:0}}@media(max-width:480px){.onboarding>header{padding:22px 20px}.onboarding li{padding:17px 20px}.onboarding>a{padding:17px 20px}}
</style>