跳转至
MIGRATED

Xisound 前端路由与 Deep-link 协议规范 v1.0

文档定位

  • 上游:D3-FE-ARCH-001 顶层架构 §7.2 新引入技术(vue-router 4) · §6.2 XiStudio deep-link 机制
  • 本文范围:7 个 app 的路由树统一规范 + xi:// URL Scheme 权威定义 + 跨 app Intent 分发机制
  • 约束强度:所有 apps 必须遵守 · 废弃现有 appMode 单体切换范式
  • 目标读者:前端工程师 / 其他智能体 / XiStudio Tool Launcher 开发者

1. 路由架构总览

1.1 路由技术选型

维度 选择 理由
路由库 vue-router 4 Vue 3 官方 · 类型完备
路由模式 history mode(HTML5 pushState) 对 SEO 友好(尤其 XiVST Marketplace 公网站点)· 支持 Deep-link 直接访问
路由懒加载 Dynamic import 首屏减小 20-40% · 每个主路由单独 chunk
Fallback SPA 模式需要服务端配置 rewrite 规则 Nginx/Cloudflare 需 try_files $uri $uri/ /index.html;

1.2 全局路由约定

所有 app 的路由遵循以下统一约定:

规则 说明
路径用 kebab-case /algo-library 不用 /algoLibrary
URL 参数用 camelCase ?projectId=xxx 不用 ?project_id=xxx
:id 表示主资源 ID /project/:id/link
/intent/:intentName Deep-link 跳转用
meta.title 必填 用于浏览器标签页标题 + 面包屑
meta.requiresAuth 默认 true · 公开页(landing/login)显式声明 false

1.3 7 产品路由树汇总

graph LR
    subgraph XiStudio["xi-studio"]
        SW[/workspace]
        SP[/project/:id/link]
        SS[/project/:id/signal ⭐]
        SC[/project/:id/compile]
        SA[/algo-library]
        SF[/forge]
        SV[/vst]
        ST[/tools]
    end

    subgraph XiForge["xi-forge (内嵌 local tabs)"]
        FM[module-designer]
        FC[code-generator]
        FU[ui-designer]
        FP[preset-editor]
        FB[publisher]
    end

    subgraph XiTune["xi-tune"]
        TS[/sessions]
        TSI[/session/:id/tuner]
        TSM[/session/:id/measurement]
        TSC[/session/:id/compare]
        TSR[/session/:id/report]
        TI[/intent/:intentName]
    end

    subgraph XiTest["xi-test"]
        TES[/suites]
        TER[/run/:id]
        TERP[/reports]
        TMT[/module-test]
        TTR[/tuning-regression]
        TEI[/intent/:intentName]
    end

    subgraph XiProbe["xi-probe"]
        PD[/devices]
        PS[/session/:id/acquire]
        PA[/session/:id/analyze]
        PR[/session/:id/report]
    end

    subgraph XiVST["xi-vst (双形态)"]
        VE[embedded: /plugins]
        VM[marketplace: /browse]
        VMD[marketplace: /plugin/:slug]
        VMDD[marketplace: /developer]
    end

    subgraph XiMind["xi-mind (非独立 app · SDK 形态)"]
        MS[(ai-sdk 作为 package 被所有 app 引用)]
    end
    class XiStudio,XiForge,XiVST xySgL4; class XiTune,XiTest,XiProbe xySgL2; class XiMind xySgL5;

2. xi:// URL Scheme 协议(权威)

2.1 Scheme 语法

xi://<app>/<route>[?<query>]

定义:

  • <app>:目标 app 标识,必须xi-studio / xi-forge / xi-vst / xi-tune / xi-test / xi-probe 之一
  • <route>:app 内部路由路径(不含前导 /),支持嵌套(session/123/tuner)或 intent(intent/auto-tune)
  • <query>:URL query string,标准编码

示例:

xi://xi-tune/intent/auto-tune?projectId=proj001&token=tmp_xyz
xi://xi-test/run/test-suite-42?autoStart=true
xi://xi-probe/session/s001/acquire
xi://xi-studio/project/proj001/signal

2.2 标准参数(所有 app 可能用到)

参数 类型 必填 说明
token string ⭐ 推荐 短时效一次性 token(60s 有效期)· 避免长 token 暴露
projectId string 关联的 XiStudio 工程 ID
sessionId string app 内部 session ID
intent string 显式意图关键字(等价于 /intent/:intentName 路径形式)
lang string zh-CN / en-US · 覆盖用户默认语言
theme string light / dark · 覆盖默认主题
autoStart boolean 进入后自动执行主动作(如自动开始测试)
returnUrl string 完成后返回的 URL(通常回到 xi-studio)
context base64 string 上下文 JSON 的 base64 编码(过大数据不建议 URL,用 IndexedDB 存 handoff)

2.3 Token 交换机制(Security)

禁止直接在 URL 中传递长期 JWT token。采用短时效一次性 token 机制:

sequenceDiagram
    participant S as XiStudio
    participant BE as Backend /api/tool-token
    participant T as XiTune
    participant Auth as Auth Service

    S->>BE: POST /api/tool-token<br/>{tool: 'xi-tune', projectId, ttl: 60s}
    BE->>Auth: 验证 Studio 当前 user 有权访问 projectId
    Auth-->>BE: OK
    BE->>BE: 生成 tmpToken(32 bytes · 60s 有效 · 单次使用)
    BE-->>S: {tmpToken, expiresAt}
    S->>T: window.open('xi://xi-tune/...&token=tmpToken')
    T->>BE: POST /api/auth/exchange-tool-token<br/>{tmpToken}
    BE->>BE: 验证 tmpToken(单次 + 未过期)
    BE->>BE: 标记 tmpToken 已使用
    BE-->>T: {userToken, user, tenant, projectContext}
    T->>T: 用 userToken 正常 API 调用

实现要点:

  • tmpToken 存后端 Redis(key: tool_token:<tmpToken>, TTL: 60s)
  • 单次使用(换取 userToken 后立即 delete)
  • 跨 app 调用日志用 toolTokenId 串联追踪
// packages/protocol/src/deep-link/parser.ts
import { z } from 'zod'

const DeepLinkSchema = z.object({
  app: z.enum(['xi-studio', 'xi-forge', 'xi-vst', 'xi-tune', 'xi-test', 'xi-probe']),
  route: z.string(),
  params: z.record(z.string()),
})

export type DeepLink = z.infer<typeof DeepLinkSchema>

export function parseDeepLink(url: string): DeepLink {
  const u = new URL(url)
  if (u.protocol !== 'xi:') {
    throw new Error(`Invalid scheme: ${u.protocol} (expected xi:)`)
  }
  const app = u.hostname as DeepLink['app']
  const route = u.pathname.replace(/^\//, '')  // 去前导 /
  const params: Record<string, string> = {}
  u.searchParams.forEach((v, k) => { params[k] = v })
  return DeepLinkSchema.parse({ app, route, params })
}

export function buildDeepLink(link: DeepLink): string {
  const qs = new URLSearchParams(link.params).toString()
  const path = link.route ? `/${link.route}` : ''
  return `xi://${link.app}${path}${qs ? `?${qs}` : ''}`
}

3. 各 app 路由树详细定义

3.1 xi-studio

// apps/xi-studio/src/router/index.ts
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: ShellLayout,
    children: [
      { path: '', redirect: '/workspace' },
      { path: 'workspace', component: WorkspaceView, meta: { title: '工作台', requiresAuth: true } },
      {
        path: 'project/:id',
        component: ProjectContextLayout,
        props: true,
        children: [
          { path: '', redirect: 'link' },
          { path: 'link', component: LinkEditorView, meta: { title: '链路编辑器(算法画布)' } },
          { path: 'signal', component: SignalFlowView, meta: { title: 'Signal Flow(信号画布)' } },   // ⭐ 新增
          { path: 'compile', component: CompileView, meta: { title: '编译烧录' } },
          { path: 'xml-tune', component: XmlTuneCompatView, meta: { title: 'XML 调音(兼容)' } },
        ],
      },
      { path: 'algo-library', component: AlgoLibraryView, meta: { title: '算法库' } },
      { path: 'algo-library/xialgo/:id', component: AlgoDetailView, props: true },
      { path: 'algo-library/xivst', component: VSTEmbeddedView, meta: { title: 'XiVST Marketplace' } },
      { path: 'algo-library/private', component: PrivateAlgoView },
      { path: 'tools', component: ToolsView, meta: { title: '工具' } },
      { path: 'forge', component: ForgePanelView, meta: { title: 'XiForge 锻造台' } },
      { path: 'settings', component: SettingsView, meta: { title: '设置' } },
    ],
  },
  { path: '/login', component: LoginView, meta: { requiresAuth: false, title: '登录' } },
  { path: '/:pathMatch(.*)*', component: NotFoundView },
]

3.2 xi-tune

// apps/xi-tune/src/router/index.ts
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: TuneLayout,
    children: [
      { path: '', redirect: '/sessions' },
      { path: 'sessions', component: SessionListView, meta: { title: '调音会话' } },
      {
        path: 'session/:id',
        component: SessionContextLayout,
        props: true,
        children: [
          { path: '', redirect: 'tuner' },
          { path: 'measurement', component: MeasurementView, meta: { title: '测量' } },
          { path: 'tuner', component: TunerView, meta: { title: '调音' } },
          { path: 'compare', component: CompareView, meta: { title: 'A/B 对比' } },
          { path: 'report', component: ReportView, meta: { title: '报告' } },
        ],
      },
      { path: 'intent/:intentName', component: IntentHandlerView, props: true },  // Deep-link 意图分发
    ],
  },
  { path: '/login', component: LoginView, meta: { requiresAuth: false } },
  { path: '/:pathMatch(.*)*', component: NotFoundView },
]

// Intent 清单
const TUNE_INTENTS = {
  'auto-tune': '自动调音(需传 projectId)',
  'measure': '直接进入测量流程',
  'load-preset': '加载调音预设(需传 presetId)',
  'blind-compare': '盲听对比(需传 scenarioIds)',
}

3.3 xi-test

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: TestLayout,
    children: [
      { path: '', redirect: '/suites' },
      { path: 'suites', component: TestSuiteListView, meta: { title: '测试套件' } },
      { path: 'suites/:id', component: TestSuiteDetailView, props: true },
      { path: 'run/:id', component: TestRunView, props: true, meta: { title: '运行测试' } },
      { path: 'reports', component: ReportsView, meta: { title: '报告' } },
      { path: 'reports/:id', component: ReportDetailView, props: true },
      { path: 'module-test', component: ModuleUnitTestView, meta: { title: '模块单测' } },
      { path: 'tuning-regression', component: TuningRegressionView },
      { path: 'intent/:intentName', component: IntentHandlerView, props: true },
    ],
  },
]

const TEST_INTENTS = {
  'run-suite': '运行指定套件(需传 suiteId + autoStart=true)',
  'regression': '对比当前工程 vs baseline',
  'inject-signal': '快速模块单测(需传 moduleId + signalType)',
}

3.4 xi-probe

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: ProbeLayout,
    children: [
      { path: '', redirect: '/devices' },
      { path: 'devices', component: DeviceManagerView, meta: { title: '设备管理' } },
      {
        path: 'session/:id',
        component: ProbeSessionLayout,
        props: true,
        children: [
          { path: '', redirect: 'acquire' },
          { path: 'acquire', component: AcquisitionView },
          { path: 'analyze', component: AnalysisView },
          { path: 'report', component: CertificationReportView },
        ],
      },
      { path: 'signal-generator', component: SignalGeneratorView },
      { path: 'intent/:intentName', component: IntentHandlerView, props: true },
    ],
  },
]

const PROBE_INTENTS = {
  'thd-test': 'THD 测试快捷入口',
  'frequency-response': '频响测量',
  'cert-report': '生成 AES/ITU 认证报告',
}

3.5 xi-vst(双形态)

内嵌形态(在 xi-studio 中):

// 通过 xi-studio 路由 /algo-library/xivst 访问
const EmbeddedRoutes: RouteRecordRaw[] = [
  { path: '', component: PluginBrowserView, meta: { title: 'XiVST 商店' } },
  { path: 'plugin/:slug', component: PluginDetailView, props: true },
]

公网 Marketplace 形态(独立站点):

// apps/xi-vst/marketplace/src/router/index.ts
const MarketplaceRoutes: RouteRecordRaw[] = [
  { path: '/', component: LandingView, meta: { requiresAuth: false } },
  { path: '/browse', component: BrowseView, meta: { title: '浏览插件' } },
  { path: '/plugin/:slug', component: PluginDetailView, props: true },
  { path: '/categories/:cat', component: CategoryView, props: true },
  { path: '/search', component: SearchView },
  {
    path: '/developer',
    component: DeveloperLayout,
    children: [
      { path: '', component: DeveloperDashboardView },
      { path: 'plugins', component: DeveloperPluginsView },
      { path: 'plugins/new', component: PluginSubmitView },
      { path: 'plugins/:id/edit', component: PluginEditView, props: true },
      { path: 'revenue', component: RevenueView },
      { path: 'analytics', component: AnalyticsView },
    ],
  },
  { path: '/docs', component: SDKDocsView },
  { path: '/docs/:slug', component: SDKDocDetailView, props: true },
]

3.6 xi-forge(内嵌 local tabs · 无独立 vue-router)

XiForge 作为 XiStudio 内嵌组件,不使用顶层 vue-router,而是用内部 <Tabs> 组件切换 5 个子页面:

<ForgePanel>
  <Tabs v-model="activeTab">
    <TabPanel name="module-designer">...</TabPanel>
    <TabPanel name="code-generator">...</TabPanel>
    <TabPanel name="ui-designer">...</TabPanel>
    <TabPanel name="preset-editor">...</TabPanel>
    <TabPanel name="publisher">...</TabPanel>
  </Tabs>
</ForgePanel>

Deep-link 呼起 XiForge 特定 tab:xi://xi-studio/forge?tab=ui-designer


4. 路由守卫与生命周期

4.1 认证守卫

// router/guards.ts
router.beforeEach(async (to, from, next) => {
  const auth = useAuthStore()
  if (to.meta.requiresAuth === false) return next()

  if (!auth.user) {
    await auth.tryRestoreSession()
    if (!auth.user) {
      return next({ path: '/login', query: { returnUrl: to.fullPath } })
    }
  }
  next()
})

4.2 未保存确认守卫

router.beforeEach((to, from, next) => {
  if (from.name === 'LinkEditor' || from.name === 'SignalFlow') {
    const linkStore = useLinkStore()
    if (linkStore.isDirty) {
      const confirmed = window.confirm('有未保存的修改,确定离开?')
      if (!confirmed) return next(false)
    }
  }
  next()
})
// views/IntentHandlerView.vue
const route = useRoute()
const intent = route.params.intentName as string
const { token, projectId, ...rest } = route.query

onMounted(async () => {
  // 1. 交换 token
  const session = await authStore.exchangeToolToken(token as string)
  // 2. 加载 context
  if (projectId) await projectStore.open(projectId as string)
  // 3. 按 intent 分发
  switch (intent) {
    case 'auto-tune':
      await sessionStore.createSession({ projectRef: projectId })
      router.replace({ path: `/session/${sessionStore.active.id}/tuner`, query: { mode: 'ai-full' } })
      break
    // ... 更多 intents
    default:
      console.warn(`Unknown intent: ${intent}`)
      router.replace('/')
  }
})

5. 跨 app 通信补充:BroadcastChannel

除 Deep-link 外,同域名打开的多个 app tab 间可通过 BroadcastChannel 广播事件(无需服务端中转):

// packages/protocol/src/bus/broadcast-channel.ts
const XI_CHANNEL = 'xi-platform-bus'

export class XiBus {
  private channel: BroadcastChannel

  constructor() {
    this.channel = new BroadcastChannel(XI_CHANNEL)
  }

  emit<K extends keyof XiBusEvents>(event: K, payload: XiBusEvents[K]): void {
    this.channel.postMessage({ event, payload, timestamp: Date.now() })
  }

  on<K extends keyof XiBusEvents>(event: K, handler: (p: XiBusEvents[K]) => void): () => void {
    const listener = (msg: MessageEvent) => {
      if (msg.data.event === event) handler(msg.data.payload)
    }
    this.channel.addEventListener('message', listener)
    return () => this.channel.removeEventListener('message', listener)
  }
}

// 事件清单(所有 app 共享)
export interface XiBusEvents {
  'project.updated': { projectId: string; source: string }
  'algo-library.refreshed': {}
  'copilot.handoff': { fromApp: string; toApp: string; intent: string; context: any }
  'signal.value-update-broadcast': { signalName: string; value: number }  // Signal I/O 全局广播
  'session.status-changed': { sessionId: string; status: string }
}

限制:同源(same origin)才可互通 · 跨域场景需退回 postMessage(iframe)或 WebSocket 中转


6. DQ 清单

DQ 编号 问题 建议
DQ-ROUTE-01 是否支持浏览器 back/forward 跨 app(history stack) 不支持跨 app history(每个 app 独立 tab);单 app 内正常支持
DQ-ROUTE-02 SPA 路由 + SEO 冲突(XiVST Marketplace 公网站点) XiVST Marketplace 单独用 SSR(Nuxt)或预渲染(vite-ssg)优化 SEO
DQ-ROUTE-03 xi:// scheme 的 Tauri OS 级注册 Y2 Tauri 化时通过 tauri.conf.json 注册 system-wide URL handler
DQ-ROUTE-04 Intent 清单如何跨 team 统一维护 在 @xi/protocol 中央定义 TUNE_INTENTS / TEST_INTENTS / PROBE_INTENTS 常量 + 文档

7. 版本与变更记录

版本 日期 作者 说明
v1.0 2026-05-08 work-cline 初稿 · 7 产品路由树 + xi:// URL Scheme 权威定义 + Token 交换机制 + Intent 分发 + BroadcastChannel

附录 A · 引用