feat(014): 周回顾竖版分享卡(Phase 4) - #9
Conversation
- Extract 9:16 dimensions, captureViewToPng, saveShareCardImage, privacyAck - Web data-uri download; native MediaLibrary save with haptic - Unit tests for dimensions ratio and privacy ack short-circuit Co-authored-by: Cursor <cursoragent@cursor.com>
- Week/resolve/burn view-models strip people, triggers, and content - userSnippet trim and 80-char cap; share namespace stub for ritual copy - Unit tests assert serialized models contain no PII substrings Co-authored-by: Cursor <cursoragent@cursor.com>
- 9:16 shell with ritual accent strip and footer watermark - Full zh-Hans/en-US share.json with copy contract keys - i18n parity tests; OpenSpec 014 Wave 1 foundation note Co-authored-by: Cursor <cursoragent@cursor.com>
- Weather + garden + AI closing zones per D-03/D-11 - weatherCard tokens and growth stage icons; no stats/PII Co-authored-by: Cursor <cursoragent@cursor.com>
- ShareCardShell + ShareCardWeekContent preview with shared capture/save - Opt-in snippet toggle; Web PNG download; remove webUnsupported path Co-authored-by: Cursor <cursoragent@cursor.com>
- ShareCardResolveContent with weather/garden/moment zones (D-18) - ShareCardBurnContent with simplified release metaphor - No entry content, people, or triggers on card surface Co-authored-by: Cursor <cursoragent@cursor.com>
- Register modal stack screen with presentation modal - ShareCardPreviewScreen with variant-only params (D-12) - Opt-in snippet toggle, capture/save via shared share layer (D-05/D-10) Co-authored-by: Cursor <cursoragent@cursor.com>
- Delay resolveEntry until share CTA tap or skip (D-07) - Inline share-card-cta-resolve after ceremony complete - Burn toast share-card-cta-burn during 2.5s visibility (D-08) - No auto navigation on ceremony complete (D-05) Co-authored-by: Cursor <cursoragent@cursor.com>
- Replace legacy canvas with @deprecated stub; export path uses ShareCardWeekContent - Fix lint: snippet left-border style, useMemo derived dependency Co-authored-by: Cursor <cursoragent@cursor.com>
- Assert captureRef logical dimensions at PixelRatio 2 - Assert zero-dimension measure rejection - Extend shareCopy for watermark distinctness and cta.generate parity Co-authored-by: Cursor <cursoragent@cursor.com>
- Resolve/burn CTA to preview with SHR-02 negative gate - Week review-export path with share-card-canvas - Add yarn test:maestro:014 script Co-authored-by: Cursor <cursoragent@cursor.com>
- review-export canvas/save CTA smoke for SHR-04 - Skip full PNG download in headless; document E2E_SHARE_WEB_DOWNLOAD gate - SHR-01–05 verification matrix and Maestro 014 command table Co-authored-by: Cursor <cursoragent@cursor.com>
Remove cancelled SHR-02 ritual share flow, fix PushNotificationIOS save crash, deep-link back navigation, card layout/readability, and preset-aware titles and weather narrative. Update Maestro 014, tests, and VERIFICATION sign-off. Co-authored-by: Cursor <cursoragent@cursor.com>
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
|
Warning Review limit reached
Next review available in: 28 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (9)
📝 WalkthroughWalkthroughIntroduces a shareable weekly review "share card" feature: a new ChangesShare card export feature
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant ReviewExportScreen
participant ensurePrivacyAck
participant captureViewToPng
participant saveShareCardImage
User->>ReviewExportScreen: Press save button
ReviewExportScreen->>ensurePrivacyAck: Gate capture on privacy acknowledgement
ensurePrivacyAck-->>ReviewExportScreen: onConfirm(shouldCapture)
ReviewExportScreen->>captureViewToPng: Capture card view to PNG
captureViewToPng-->>ReviewExportScreen: Return capture URI
ReviewExportScreen->>saveShareCardImage: Save image (uri, copy)
saveShareCardImage-->>User: Show success or permission alert
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (7)
components/ReviewExport/ReviewExportScreen.tsx (1)
310-314: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
logger.errorfor save failures. Route this catch throughutils/loggerinstead ofconsole.errorso it goes through the shared logging path.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/ReviewExport/ReviewExportScreen.tsx` around lines 310 - 314, The save-failure catch in ReviewExportScreen should use the shared logging path instead of console.error. Update the onPress handler around onPressSave() to import and call logger.error from utils/logger inside the catch block, preserving the error details and a clear message so save failures are routed through the standard logger.Source: Coding guidelines
shared/share/saveShareCardImage.ts (1)
23-23: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueMinor: prefer
remove()overremoveChild.♻️ Proposed fix
- document.body.removeChild(anchor); + anchor.remove();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/share/saveShareCardImage.ts` at line 23, The share card download cleanup in saveShareCardImage should use the element’s remove() method instead of calling document.body.removeChild(anchor). Update the anchor cleanup near saveShareCardImage so the temporary anchor is removed directly via anchor.remove(), keeping the rest of the download flow unchanged.Source: Linters/SAST tools
shared/share/shareCardDimensions.ts (1)
1-8: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSimplify re-export using
export … from.The import-then-re-export pattern is unnecessary since the constants aren't used elsewhere in this file.
♻️ Proposed simplification
-import { - SHARE_CARD_ASPECT_RATIO, - SHARE_CARD_HEIGHT_PX, - SHARE_CARD_WIDTH_PX, -} from "../../constants/performance"; - -export { SHARE_CARD_ASPECT_RATIO, SHARE_CARD_HEIGHT_PX, SHARE_CARD_WIDTH_PX }; +export { + SHARE_CARD_ASPECT_RATIO, + SHARE_CARD_HEIGHT_PX, + SHARE_CARD_WIDTH_PX, +} from "../../constants/performance";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/share/shareCardDimensions.ts` around lines 1 - 8, The file only re-exports SHARE_CARD_ASPECT_RATIO, SHARE_CARD_HEIGHT_PX, and SHARE_CARD_WIDTH_PX, so the current import-then-export pattern is unnecessary. Remove the unused import block in shareCardDimensions and switch to a direct re-export from "../../constants/performance" so the module uses export ... from instead of importing constants just to export them.Source: Linters/SAST tools
shared/share/captureViewToPng.ts (1)
10-10: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRedundant type alias.
CaptureViewResultis just an alias forstringwith no added semantics.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/share/captureViewToPng.ts` at line 10, Remove the redundant CaptureViewResult type alias in captureViewToPng and use string directly where this type is referenced, since it adds no semantics. Update any signatures or exports that depend on CaptureViewResult so the code continues to compile while eliminating the unnecessary alias.Source: Linters/SAST tools
shared/share/buildShareCardModel.ts (3)
88-90: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
as nevercast bypasses i18next key typing.Casting
narrative.narrativeKey as neverto satisfyt()'s typed keys defeats the purpose of typed translation keys and could silently mask a missing/incorrect key. Consider typingcomputeWeatherNarrative'snarrativeKeyreturn as a literal union of validdashboardnamespace keys instead of casting.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/share/buildShareCardModel.ts` around lines 88 - 90, The `as never` cast in `computeWeatherNarrative` is bypassing the typed `dashboardT(locale)` translation key checks. Remove the cast by tightening `computeWeatherNarrative` so `narrative.narrativeKey` is inferred as a literal union of valid dashboard namespace keys, and pass that typed key directly to `t()` so the compiler can catch invalid or missing translation keys.
58-67: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract nested ternary for readability.
SonarCloud flags the nested ternary chain computing
stage. A lookup table or explicit if/else improves readability without behavior change.♻️ Suggested refactor
- const stage: GrowthStageId = - (rate ?? 0) >= 0.8 - ? "bloom" - : (rate ?? 0) >= 0.6 - ? "bud" - : (rate ?? 0) >= 0.4 - ? "seedling" - : (rate ?? 0) >= 0.2 - ? "sprout" - : "seed"; + const resolutionRate = rate ?? 0; + let stage: GrowthStageId = "seed"; + if (resolutionRate >= 0.8) stage = "bloom"; + else if (resolutionRate >= 0.6) stage = "bud"; + else if (resolutionRate >= 0.4) stage = "seedling"; + else if (resolutionRate >= 0.2) stage = "sprout";🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/share/buildShareCardModel.ts` around lines 58 - 67, Refactor the nested ternary used to compute stage in buildShareCardModel into a more readable form without changing behavior. Replace the chained conditional expression in the stage assignment with either an explicit if/else ladder or a small lookup/helper based on rate so the GrowthStageId mapping remains identical. Keep the existing stage thresholds and reference the stage calculation in buildShareCardModel when updating the logic.Source: Linters/SAST tools
14-14: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
weatherBucketunion includes an unreachableWeatherConditionmember.
buildWeekShareCardModelonly ever assignsdominantBucket, which is typed asExportWeatherBucket(Lines 98-99). TheWeatherConditionbranch of the union is never produced here, yet consumers likeShareCardWeekContent.resolveWeatherBucketmust defensively guard against it. Consider narrowing the type toExportWeatherBucketunless another producer ofWeatherConditionvalues is planned.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@shared/share/buildShareCardModel.ts` at line 14, The weatherBucket type in buildShareCardModel is too broad because buildWeekShareCardModel only assigns dominantBucket, which is already an ExportWeatherBucket. Narrow weatherBucket from the ExportWeatherBucket | WeatherCondition union to ExportWeatherBucket in the shared model, and then update any affected consumers such as ShareCardWeekContent.resolveWeatherBucket to remove the unreachable WeatherCondition handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.maestro/flows/014-shareable-ritual-cards.yaml:
- Around line 52-58: The back-navigation step in the shareable ritual cards flow
is hardcoded to a locale-specific text match, which conflicts with the
testID-only convention. Update the flow to use a stable identifier by adding a
dedicated testID for the back button in the export review screen and changing
the `tapOn` step in this ritual flow to target that testID instead of matching
the “返回” label. Keep the existing `extendedWaitUntil` and screenshot steps
unchanged, and reference the back-button control in the review/export screen
when applying the fix.
In `@components/ReviewExport/ReviewExportScreen.tsx`:
- Around line 126-133: Fix the hook dependency warning in ReviewExportScreen by
ensuring the useMemo that builds periodEntries depends on stable range
primitives instead of derived.current directly. Extract startMs and endMs from
derived.current before the memo (or otherwise include the expected primitive
dependency) and update the dependency array for periodEntries so eslint/pr-gate
no longer flags it. After the change, run lint/typecheck to verify the hook
warning is cleared.
- Around line 408-426: Add accessibility labels to the snippet controls in
ReviewExportScreen so screen readers can identify them. Update the Switch with
an accessibilityLabel (or equivalent accessible metadata) tied to the snippet
enablement text, and add a clear accessibilityLabel to the TextInput for the
snippet text. Use the existing snippet-related identifiers like snippetEnabled,
setSnippetEnabled, snippetText, and the
share-card-snippet-toggle/share-card-snippet-input controls to keep the labels
consistent with the surrounding UI.
In `@locales/en-US/share.json`:
- Around line 15-22: Remove the obsolete SHR-02 share-card locale entries from
the share JSON files: delete the canvas.resolve/canvas.burn and
preview.titleResolve/preview.titleBurn strings from the share translation
objects in both locale files. Keep the remaining share-card keys intact, and
ensure any references in the share-card/preview flow no longer depend on these
removed keys.
In `@openspec/changes/014-shareable-ritual-cards/SPEC.md`:
- Around line 1-99: The change scope is cross-module and requires a PLAN to
coordinate implementation across the new share card modules, the
ReviewExportScreen wiring, i18n additions, and the deprecated canvas path. Add a
PLAN.md for this cohort using the openspec template style, and make it reference
the key symbols involved in the rollout such as ReviewExportScreen,
ShareCardShell, ShareCardWeekContent, buildShareCardModel, captureViewToPng, and
saveShareCardImage so the implementation steps are clear and traceable.
In `@openspec/changes/014-shareable-ritual-cards/UI-SPEC.md`:
- Line 288: The table row in the shareable ritual cards spec is being split by
the unescaped pipe in `variant=resolve|burn`, which breaks the markdown column
layout. Update the `Params` cell content to avoid a raw `|` inside the table row
by escaping it or rephrasing the variant list so the row still renders as
exactly two columns.
In `@shared/share/saveShareCardImage.ts`:
- Around line 46-48: The error handling in saveShareCardImage should stop using
console.error and route through utils/logger instead. Update the
Linking.openSettings().catch block to log the failure via the shared logger with
appropriate error-level context, and keep any nonessential output gated to
__DEV__ if needed. Use the existing logger utility in
shared/share/saveShareCardImage.ts so new logging follows repo conventions and
avoids raw console output in production.
---
Nitpick comments:
In `@components/ReviewExport/ReviewExportScreen.tsx`:
- Around line 310-314: The save-failure catch in ReviewExportScreen should use
the shared logging path instead of console.error. Update the onPress handler
around onPressSave() to import and call logger.error from utils/logger inside
the catch block, preserving the error details and a clear message so save
failures are routed through the standard logger.
In `@shared/share/buildShareCardModel.ts`:
- Around line 88-90: The `as never` cast in `computeWeatherNarrative` is
bypassing the typed `dashboardT(locale)` translation key checks. Remove the cast
by tightening `computeWeatherNarrative` so `narrative.narrativeKey` is inferred
as a literal union of valid dashboard namespace keys, and pass that typed key
directly to `t()` so the compiler can catch invalid or missing translation keys.
- Around line 58-67: Refactor the nested ternary used to compute stage in
buildShareCardModel into a more readable form without changing behavior. Replace
the chained conditional expression in the stage assignment with either an
explicit if/else ladder or a small lookup/helper based on rate so the
GrowthStageId mapping remains identical. Keep the existing stage thresholds and
reference the stage calculation in buildShareCardModel when updating the logic.
- Line 14: The weatherBucket type in buildShareCardModel is too broad because
buildWeekShareCardModel only assigns dominantBucket, which is already an
ExportWeatherBucket. Narrow weatherBucket from the ExportWeatherBucket |
WeatherCondition union to ExportWeatherBucket in the shared model, and then
update any affected consumers such as ShareCardWeekContent.resolveWeatherBucket
to remove the unreachable WeatherCondition handling.
In `@shared/share/captureViewToPng.ts`:
- Line 10: Remove the redundant CaptureViewResult type alias in captureViewToPng
and use string directly where this type is referenced, since it adds no
semantics. Update any signatures or exports that depend on CaptureViewResult so
the code continues to compile while eliminating the unnecessary alias.
In `@shared/share/saveShareCardImage.ts`:
- Line 23: The share card download cleanup in saveShareCardImage should use the
element’s remove() method instead of calling document.body.removeChild(anchor).
Update the anchor cleanup near saveShareCardImage so the temporary anchor is
removed directly via anchor.remove(), keeping the rest of the download flow
unchanged.
In `@shared/share/shareCardDimensions.ts`:
- Around line 1-8: The file only re-exports SHARE_CARD_ASPECT_RATIO,
SHARE_CARD_HEIGHT_PX, and SHARE_CARD_WIDTH_PX, so the current import-then-export
pattern is unnecessary. Remove the unused import block in shareCardDimensions
and switch to a direct re-export from "../../constants/performance" so the
module uses export ... from instead of importing constants just to export them.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 9e1f73aa-104c-496f-9d4c-e6f276f5ba30
📒 Files selected for processing (29)
.maestro/config.yaml.maestro/flows/014-shareable-ritual-cards.yaml__tests__/unit/i18n/namespaceKeys.test.ts__tests__/unit/i18n/shareCopy.test.ts__tests__/unit/shared/share/buildShareCardModel.test.ts__tests__/unit/shared/share/captureViewToPng.test.ts__tests__/unit/shared/share/privacyAck.test.ts__tests__/unit/shared/share/saveShareCardImage.test.ts__tests__/unit/shared/share/shareCardDimensions.test.tscomponents/EntryCard.tsxcomponents/ReviewExport/ReviewExportCanvas.tsxcomponents/ReviewExport/ReviewExportScreen.tsxcomponents/share/ShareCardShell.tsxcomponents/share/ShareCardWeekContent.tsxconstants/performance.tse2e/share-card-web-download.spec.tsi18n/index.tslocales/en-US/share.jsonlocales/zh-Hans/share.jsonopenspec/changes/014-shareable-ritual-cards/SPEC.mdopenspec/changes/014-shareable-ritual-cards/UI-SPEC.mdopenspec/changes/014-shareable-ritual-cards/VERIFICATION.mdpackage.jsonshared/share/buildShareCardModel.tsshared/share/captureViewToPng.tsshared/share/privacyAck.tsshared/share/saveShareCardImage.tsshared/share/shareCardDimensions.tstypes/i18next.d.ts
| # SPEC:014 可分享周回顾卡片(shareable-ritual-cards) | ||
|
|
||
| ## 背景 | ||
|
|
||
| - **当前问题:** `ReviewExportScreen` 为横版自适应画布,含触发器 TopN 等统计块;Web 导出仅 `webUnsupported` Alert。 | ||
| - **用户影响:** 用户无法将周回顾以温和、脱敏的竖版卡片存入相册传播;与 v1.4「仪式可分享」差异化目标未完全达成(仪式时刻分享已移出本 Phase)。 | ||
| - **相关代码:** `components/ReviewExport/ReviewExportScreen.tsx`、`components/ReviewExport/ReviewExportCanvas.tsx`、`shared/weather/weatherNarrative.ts`、`utils/reviewExportDerived.ts`。 | ||
| - **相关文档:** `.planning/REQUIREMENTS.md` SHR-01、SHR-03–SHR-05;`openspec/changes/011-metaphor-activation/VISUAL-IDENTITY.md` §6。 | ||
|
|
||
| ## 目标 | ||
|
|
||
| | ID | 能力 | 实现要点 | | ||
| |----|------|----------| | ||
| | SHR-01 | 周回顾竖版 share card 存相册 | 固定 9:16 `ShareCardShell` + `ShareCardWeekContent`;重构 `ReviewExportScreen` 捕获链路 | | ||
| | SHR-03 | 011 视觉;默认无日记正文 | 粉壳;`buildShareCardModel` 脱敏;opt-in snippet 默认 off | | ||
| | SHR-04 | iOS/Android 相册;Web 下载 | `shared/share/captureViewToPng` + `saveShareCardImage`;复用 `review_export_privacy_ack_v1` | | ||
| | SHR-05 | 适度 app 水印 | Shell footer `share.watermark.brand` + 可选日期;secondary 色低调 | | ||
|
|
||
| ### 不在本次范围 | ||
|
|
||
| - **SHR-02 和解/焚烧仪式分享卡**(已取消;和解/焚烧恢复 Phase 3 行为,无分享 CTA) | ||
| - 应用内社交 feed、评论、点赞 | ||
| - 系统 `Share.share` sheet(D-16 deferred) | ||
| - 二维码 / App Store 短链水印 | ||
| - 卡片展示人名、触发器、日记全文(永久拒绝) | ||
| - 卡片中英双语同显(跟随 locale 单语) | ||
| - `ReviewExportCanvas` 触发器 TopN / 解决率大图(D-11 废弃) | ||
| - Phase 5 RevisitBanner / WeeklyReview 动态 copy(RET-*) | ||
|
|
||
| ## 用户行为 | ||
|
|
||
| ### 触发入口 | ||
|
|
||
| 1. **周回顾:** Insights / WeeklyReviewBanner → `review-export` → 选 preset → 预览 9:16 卡 → 保存相册 / Web 下载。 | ||
|
|
||
| ### 期望结果 | ||
|
|
||
| - 导出 PNG **1080×1920**(9:16),WYSIWYG 预览与成片一致。 | ||
| - 默认卡片仅气象 + 花园隐喻 + AI 结语;用户勾选 opt-in 后才出现自定义一句(max 80 字)。 | ||
| - 首次存相册/Web 下载经隐私确认;后续复用同一 AsyncStorage key。 | ||
| - 用户自行从相册分享到社交平台;应用内不代为发帖。 | ||
|
|
||
| ### 异常或边界 | ||
|
|
||
| - AI 周卡结语 loading 时保存钮 disabled。 | ||
| - 相册权限拒绝 → Alert + 打开设置(复用 review 权限文案)。 | ||
| - Web `captureRef` 失败 → toast/Alert + 保留预览。 | ||
|
|
||
| ## 技术约束 | ||
|
|
||
| - **架构:** `shared/share/` 捕获/保存/隐私/model;`components/share/` 外壳 + `ShareCardWeekContent`;`ReviewExportScreen` 为唯一预览入口。 | ||
| - **Wave 1 基础(04-01):** `shared/share/shareCardDimensions.ts`、`captureViewToPng.ts`、`saveShareCardImage.ts`、`privacyAck.ts`、`buildShareCardModel.ts`;`components/share/ShareCardShell.tsx`;`locales/*/share.json` + i18n `share` namespace。 | ||
| - **数据:** view-model 仅含枚举、i18n 已解析句、聚合隐喻;**禁止** `entry.content` / `people` / `triggers` 上卡。 | ||
| - **i18n:** 新 `share` namespace(zh-Hans / en-US);卡片文案跟随 `effectiveLocale`(D-04)。 | ||
| - **多端:** iOS/Android `expo-media-library`;Web `captureRef` `data-uri` + anchor download(D-14)。 | ||
| - **视觉:** 011 粉壳(D-18 仪式 accent 边条仅适用于未来仪式 variant,本 Phase 仅 week)。 | ||
| - **E2E:** Maestro `014-shareable-ritual-cards` 仅覆盖 `review-export` 路径;Playwright Web download smoke。 | ||
| - **分支:** `260702-feat-shareable-ritual-cards`(从 `master` 切出)。 | ||
|
|
||
| ## 锁定决策(CONTEXT D-01–D-18,本 Phase 适用子集) | ||
|
|
||
| | ID | 决策 | 本 Phase | | ||
| |----|------|----------| | ||
| | D-01 | 固定竖版 9:16,导出 1080×1920 PNG | ✅ | | ||
| | D-02 | 统一粉系外壳 + week 内容变体 | ✅(仅 week) | | ||
| | D-03 | 视觉重心 = 气象 + 花园隐喻,非数据表格 | ✅ | | ||
| | D-04 | 卡片文案跟随 App 当前 locale | ✅ | | ||
| | D-05–D-08 | 仪式完成后内嵌分享 CTA | ❌ 已取消 | | ||
| | D-09 | 默认 = 纯隐喻句,无日记原文 | ✅ | | ||
| | D-10 | 预览页 opt-in「加一句自己的话」;默认关闭 | ✅ | | ||
| | D-11 | 周卡 = 天气隐喻 + 花园 + AI 结语;无 Top 触发器/统计块 | ✅ | | ||
| | D-12 | 永不展示人名、触发器、可识别关系标签 | ✅ | | ||
| | D-13 | iOS/Android 主路径存系统相册 | ✅ | | ||
| | D-14 | Web = PNG 下载(非 unsupported Alert) | ✅ | | ||
| | D-15 | 复用 `review_export_privacy_ack_v1` | ✅ | | ||
| | D-16 | 不做系统 Share sheet | ✅ deferred | | ||
| | D-17 | 水印 = 底部一行小字 + 可选日期 | ✅ | | ||
| | D-18 | 仪式 accent 边条 | ❌ 本 Phase 无 resolve/burn variant | | ||
|
|
||
| ## 验收标准 | ||
|
|
||
| - [ ] review-export 可生成 1080×1920 竖版 PNG 并存相册(SHR-01) | ||
| - [ ] 卡片 011 粉壳;默认无正文;opt-in 默认 off;无 PII(SHR-03) | ||
| - [ ] iOS/Android 相册完整;Web PNG 下载;隐私 ack 复用(SHR-04) | ||
| - [ ] footer 水印低调(SHR-05) | ||
| - [ ] `buildShareCardModel` unit 断言无 people/triggers/content 字段 | ||
| - [ ] Maestro `014-shareable-ritual-cards` 绿(仅 review-export);`yarn test:maestro:014` script 存在 | ||
| - [ ] Playwright `e2e/share-card-web-download.spec.ts` 绿 | ||
| - [ ] `yarn typecheck && yarn lint && yarn test` CI 绿 | ||
|
|
||
| ## 依据 | ||
|
|
||
| - **产品:** `.planning/ROADMAP.md` Phase 4;`.planning/REQUIREMENTS.md` SHR-* | ||
| - **技术:** `.planning/phases/04-shareable-ritual-cards/04-RESEARCH.md`、`04-PATTERNS.md`、`04-UI-SPEC.md` | ||
| - **视觉:** `openspec/changes/011-metaphor-activation/UI-SPEC.md`、`VISUAL-IDENTITY.md` §6 | ||
|
|
||
| ## 变更记录 | ||
|
|
||
| - **2026-07-01:** 移除 SHR-02 和解/焚烧仪式分享卡;Maestro 014 收窄为周回顾路径 only。 |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Missing PLAN.md for this complex change.
This change spans multiple new shared modules, screen rewiring, i18n namespace additions, and deprecation of an existing component — a cross-module refactor. Only SPEC.md, UI-SPEC.md, and VERIFICATION.md are present in this cohort.
As per coding guidelines, "For new features, important fixes, or cross-module refactoring, create a SPEC.md file in openspec/changes/<number>-<name>/; use templates from openspec/templates/ and add PLAN.md and VERIFICATION.md for complex tasks."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@openspec/changes/014-shareable-ritual-cards/SPEC.md` around lines 1 - 99, The
change scope is cross-module and requires a PLAN to coordinate implementation
across the new share card modules, the ReviewExportScreen wiring, i18n
additions, and the deprecated canvas path. Add a PLAN.md for this cohort using
the openspec template style, and make it reference the key symbols involved in
the rollout such as ReviewExportScreen, ShareCardShell, ShareCardWeekContent,
buildShareCardModel, captureViewToPng, and saveShareCardImage so the
implementation steps are clear and traceable.
Source: Coding guidelines
Add review-export-back-button for Maestro testID-only navigation, fix periodEntries hook deps, remove SHR-02 orphan share keys, and route errors through utils/logger per project conventions. Co-authored-by: Cursor <cursoragent@cursor.com>
|



Summary
ReviewExportScreen预览 + 存相册 / Web 下载(SHR-01/03–05)shared/share/捕获/保存/隐私/model 与ShareCardShell+ShareCardWeekContent014-shareable-ritual-cards、Playwright Web smoke、347 单测通过Test plan
yarn typecheck && yarn lint && yarn test(53 suites / 347 tests)yarn test:e2e e2e/share-card-web-download.spec.ts(1 pass / 1 skip headless)yarn test:maestro:014(review-export → 保存 → 返回首页)Made with Cursor
Summary by CodeRabbit
New Features
Bug Fixes
Tests