7.4 KiB
Raw Permalink Blame History

phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
phase plan type wave depends_on files_modified autonomous requirements must_haves
03-dialog-feedback 01 execute 1
app/layout.tsx
true
CRED-FE-05
truths artifacts key_links
调用 toast.success(...) / toast.error(...) 后屏幕能看到 Sonner 通知
Toaster 在所有路由下均可用(挂在 RootLayout 顶层)
不破坏现有 RootLayout 渲染children 仍正常显示)
path provides contains
app/layout.tsx RootLayout 注入 <Toaster /> portal Toaster
from to via pattern
app/layout.tsx @/components/ui/sonner import { Toaster } from "@/components/ui/sonner"
在 `app/layout.tsx` 的 `` 内挂载 Sonner ``,让全局 `toast.success(...)` / `toast.error(...)` 命令式调用真正能在屏幕上显示。

Purpose:仓库 9 处 toast(...) 调用当前全部是 dead codecomponents/ui/sonner.tsx 定义了 Toaster 包装但从未挂载),不挂载 Phase 3 的成功 / 失败反馈完全静默;这是 Phase 3 业务功能跑通的硬前置。

Output:修改后的 app/layout.tsx,新增 1 行 import + 1 个 JSX 元素挂载点。

<execution_context> @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md </execution_context>

@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/03-dialog-feedback/03-CONTEXT.md @.planning/phases/03-dialog-feedback/03-RESEARCH.md @CLAUDE.md @app/layout.tsx @components/ui/sonner.tsx

From components/ui/sonner.tsx:

type ToasterProps = React.ComponentProps<typeof Sonner>
const Toaster: ({ ...props }: ToasterProps) => JSX.Element
export { Toaster }

调用形态:<Toaster />(无 props 即可theme 内部已读 next-themes context本仓库未挂 ThemeProvider会回退到默认 "system",无错误)

Task 1在 RootLayout 挂载 Sonner Toaster app/layout.tsx 必读 `app/layout.tsx` 全文(仅 20 行)确认当前结构。当前内容:
```tsx
import type { Metadata } from 'next'
import './globals.css'

export const metadata: Metadata = {
  title: 'v0 App',
  description: 'Created with v0',
  generator: 'v0.dev',
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}
```

</read_first> 精确改动 2 处

改动 1:在 import './globals.css' 之后追加 1 行 import

import { Toaster } from '@/components/ui/sonner'

改动 2:把 <body>{children}</body> 改为 <body>{children}<Toaster /></body>(即在 {children} 之后、</body> 之前插入 <Toaster />)。

最终全文(应该是这样)

import type { Metadata } from 'next'
import './globals.css'
import { Toaster } from '@/components/ui/sonner'

export const metadata: Metadata = {
  title: 'v0 App',
  description: 'Created with v0',
  generator: 'v0.dev',
}

export default function RootLayout({
  children,
}: Readonly<{
  children: React.ReactNode
}>) {
  return (
    <html lang="en">
      <body>
        {children}
        <Toaster />
      </body>
    </html>
  )
}

严格约束

  • 挂 Radix Toast <Toaster />(来自 @/components/ui/toaster)—— 那是另一套实现,与 Sonner 不通信;本 phase 锁定 SonnerCONTEXT D-Toast 决策)

  • 新增 ThemeProvider / next-themes 包装 —— components/ui/sonner.tsx:9useTheme() fallback 到 "system",无 ThemeProvider 也能跑(本仓库目前确实没挂 ThemeProvider

  • metadata / html lang / globals.css import 顺序

  • 给 RootLayout 加 "use client" —— <Toaster /> 自身就是 client componentcomponents/ui/sonner.tsx:1 顶部已 "use client"React Server Component 可以直接渲染 client child无需 RootLayout 自己 client 化

  • 这是本 phase 唯一改动 app/layout.tsx 的 task在此挂任何其他 provider # Windows PowerShell项目不含 .eslintrc*lint 跳过沿用 Phase 1+2 判定) cd C:\Users\admin\Desktop\Lila-Server\qy-lty-admin

    # A 段tsc 整体 + 反向断言(必须 0 条指向 app/layout.tsx
    npx tsc --noEmit 2>&1 | Select-String -Pattern 'app/layout\.tsx|app\\layout\.tsx'
    # 期望无任何输出0 条新错误指向本文件)
    
    # B 段grep 验证 import + Toaster 元素都已落地PowerShell Select-String
    Select-String -Path 'app/layout.tsx' -Pattern 'from "@/components/ui/sonner"'
    # 期望1 行命中 import 行
    Select-String -Path 'app/layout.tsx' -Pattern '<Toaster\s*/>'
    # 期望1 行命中 <Toaster /> 标签
    
    # C 段lockfile 未动(不引入新依赖)—— Sonner 已在 deps^1.7.1
    git diff --stat HEAD -- package.json yarn.lock package-lock.json pnpm-lock.yaml
    # 期望0 行(无 diff
    
    - `app/layout.tsx` 包含 `import { Toaster } from "@/components/ui/sonner"` 一行 - `` 内 `{children}` 之后渲染 `` - `npx tsc --noEmit` 输出过滤 `app/layout.tsx` 后 **0 条新错误**67 条存量错误与本 task 无关) - 4 个 manifest+lockfile 在 git diff 中 0 行 diff不引入新依赖
**Phase 3 Plan 1 整体验证**Plan 内已涵盖,此处汇总):
  1. 类型检查npx tsc --noEmit exit 非 067 条存量错误,与本 phase 无关),但 Select-String 过滤 app/layout.tsx 命中 0 行
  2. 挂载位置正确grep <Toaster /> 命中且位于 <body> 内、{children} 之后(不是 <head> 内、不在 children 之前)
  3. 不动 lockfilegit diff --stat HEAD -- package.json *.lock 输出 0 行
  4. lint 跳过:项目无 .eslintrc* / eslint-config-next,沿用 Phase 1+2 判定(不阻塞)

关键失败模式(如果出现,回头修):

  • 如果挂在 <html> 之外或 <head> 内 → 渲染失败 → 修
  • 如果误用 import { Toaster } from "@/components/ui/toaster"Radix Toast→ 与 Sonner toast() 不通信 → 修
  • 如果给 layout.tsx 加了 "use client" → 改回 RSC无必要

<success_criteria>

  • app/layout.tsx import 块包含 from "@/components/ui/sonner"
  • app/layout.tsx <body> 内含 <Toaster /> 元素
  • npx tsc --noEmit 过滤后 0 条新错误指向 app/layout.tsx
  • 4 个 lockfile 在 git diff 中 0 行 diff
  • Plan 03-02 的 toast 调用上线后能在屏幕显示(本 plan 单独无法跑通端到端,由 03-02 联动验证) </success_criteria>
完成后创建 `.planning/phases/03-dialog-feedback/03-01-SUMMARY.md`,按 `$HOME/.claude/get-shit-done/templates/summary.md` 格式记录: - 改动文件清单1 个文件) - import + JSX 两处具体行号 - tsc 过滤结果 + lockfile diff 结果 - 阻塞 / 非阻塞结论 - 下一步:执行 Plan 03-02