Add sticker types, page duplication, and UI improvements#6
Conversation
## 上流(Web版)の変更の取り込み - コピー機能: 手帳まるごと(表紙の📄)とページ単位(タブの📄)の複製。 Web版のJSONディープコピーと違い、シール・テキスト・写真・工程すべてに 新しいidを振り直し、矢印の参照も新しいシェイプidへ張り替える - シール6種類を追加(くも・三日月・王冠・ちょうちょ・にくきゅう・宝石)→ 全11種類 - if分岐は画面幅によらず常に縦1カラム。RN版は元からこの挙動だが、 意図としてコメントに残した(Web版の resize 対応textareaは、RNの multiline TextInputが自動で高さを合わせるため移植不要) ## html版とのインポート互換の打ち切り - バックアップJSONを format/formatVersion を持つ独自の封筒形式に変更。 封筒のないJSON(=Web版の書き出し)は「バックアップではない」として弾く - rollbahn型名の読み替え、notestyleのnote→シェイプ移行、写真のimage別名 といったWeb版専用の移行コードを削除 ## バグ修正 - 編集画面を開いた1フレーム目に「手帳が見つかりませんでした」が出ていた。 ルートのidから直接手帳を引き、遷移前にストア側も確定させる - つなぎ線を動かす・伸ばす・回す手段が無く、updateArrow/resetArrowが 到達不能だった。Transformableに載せて手動配置と↺の復帰を有効化 - ページ切替や手帳の開閉だけで「元に戻す」履歴が消えていた。履歴の比較から activeNotebookId/activePageIdを外し、手帳・ページの削除や取り込みも 戻せるようにしたうえで、一覧画面にも取り消しボタンを置いた - ReduceMotion.Never で端末の「視差効果を減らす」設定を無視していた。 System に直し、きらめきと紙吹雪も設定を尊重する(Web版のprefers-reduced-motion相当) - 分岐の中の工程を並べ替えられなかった(moveStepが添字ベースで最上位専用)。 id基準のmoveStepInTreeにして木のどこでも並べ替え可能に - シールが盤面のパディングぶんずれて配置されていた(Web版の.sticker-layerと 同じ座標系になるよう、盤面いっぱいのレイヤーへ移動) - 表紙の淡い色(はちみつ・グレー等)の上の白文字が読みづらかった → 輝度で判定 - 同じ日に2回書き出すとバックアップのファイル名が衝突していた(分まで入れる) - 画像の共有ファイル名が "." や ".." になりうる穴を塞ぎ、日付を付ける - react-native-svgのDefsのidが固定で、複数同時描画時に衝突しうる → useIdで一意化 - normalizePageのpaperColor既定値がnewPageと食い違っていた(#FBF7F2→#FFFFFF) ## UI・デザイン - 表紙カードを「本」として作り直し(背表紙・マステで留めた名前ラベル・ ページ数・押しやすいボタン帯)。作成パネルには表紙のライブプレビューを追加 - マスキングテープを唯一の主役の飾りとして表紙だけに使う(SVGで自作) - ページタブを見出しインデックス風に。開いているタブに手帳の色の帯が乗る - Web版のグラデーションボタンをSVGで再現 - 工程表の上に進み具合(3/7・満了で🎉)を表示 - ツールバーをシール帯+道具に整理し、読み込み・消去は「⋯」シートへ - 追加した工程・条件・テキストに自動でフォーカスが移る(Web版と同じ) - チェック・シール貼り・コピーに触覚フィードバック(未使用だったexpo-hapticsを活用) - アイコンボタンのタップ判定を最低32dpに広げ、読み上げラベルを全面的に付与 - 破壊的な操作の文言を「戻せる」ことが分かる書き方に統一 ## リファクタリング - theme.tsを色・字・余白・角丸・影のトークン体系に拡張し、各コンポーネントに 散っていた生の色リテラルとフォント指定をすべて集約 - 純粋ロジックを分離: color.ts(混色・輝度)、backupFormat.ts(封筒の組立と検証)、 haptics.ts。いずれもカバレッジ100%の対象に追加 - CoverArt / StepActions / Gradient / WashiTape を抽出して重複を解消 - StickerShapeを1つのレジストリに集約(トレイ・盤面・紙吹雪・飾り枠が同じ定義を共有) - 未使用だったfindStepInTree・fileStamp等のデッドコードと重複プロップを整理 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VhmTi5pjG9aQVypwr6qSGN
|
Warning Review limit reached
Next review available in: 25 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: Organization UI Review profile: QUIET Plan: Pro Plus Run ID: 📒 Files selected for processing (14)
📝 WalkthroughWalkthroughAndroid 0.3.0 updates notebook creation and duplication, nested flowcharts, freeform editing, backups, stickers, accessibility, theming, reduced-motion behavior, testing utilities, and release metadata. ChangesAndroid 0.3.0
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant CoverScreen
participant NotebookStore
participant NotebookEditor
participant BackupFlow
User->>CoverScreen: create, duplicate, delete, or open notebook
CoverScreen->>NotebookStore: execute notebook action
NotebookStore-->>CoverScreen: return ID, success, or undo state
CoverScreen->>NotebookEditor: navigate with notebook ID
User->>NotebookEditor: edit page or request backup
NotebookEditor->>NotebookStore: commit page changes
NotebookEditor->>BackupFlow: export or import JSON
BackupFlow->>NotebookStore: apply validated state
Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
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.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0fc4566ab
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <View style={styles.body}>{children}</View> | ||
| <View | ||
| style={styles.body} | ||
| accessible={label !== undefined} |
There was a problem hiding this comment.
Keep editable text outside the accessible wrapper
When ShapeCard supplies a label, this accessible view groups its descendant TextInput into one generic accessibility element. On Android with TalkBack enabled, users can no longer focus the text field independently as an editable control, blocking freeform-text editing; only non-interactive sticker, photo, and arrow bodies should be grouped this way.
Useful? React with 👍 / 👎.
| selected={selected} | ||
| bodyDraggable | ||
| handleTint={chic.handle} | ||
| boundsWidth={boundsWidth} |
There was a problem hiding this comment.
Do not clamp rotated arrows to the canvas width
Passing boundsWidth makes Transformable clamp the arrow's length to the horizontal canvas width, even though w represents the arrow's rotated Euclidean length. For shapes separated vertically or diagonally by more than the canvas width, the previously endpoint-to-endpoint arrow is rendered shortened and no longer connects the two shapes; arrow positioning needs rotation-aware bounds or should avoid this width clamp.
Useful? React with 👍 / 👎.
| export function parseBackup(raw: unknown): BackupParseResult { | ||
| if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return { ok: false, reason: "notBackup" }; | ||
| const envelope = raw as Partial<BackupEnvelope>; | ||
| if (envelope.format !== BACKUP_FORMAT) return { ok: false, reason: "notBackup" }; |
There was a problem hiding this comment.
Accept backups from the previous Android release
The immediately preceding Android version's exportBackup serialized a bare AppState, without this new envelope—the same representation now described as Web-only. Consequently, every backup created by Android 0.2.1 is rejected here as notBackup after upgrading to 0.3.0, preventing users from restoring their existing recovery files; recognize and normalize the legacy top-level { activeNotebookId, notebooks } form.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
指摘の事実関係は正しく、v0.1.0〜v0.2.1 のAPKも実際にリリース済みなので、0.2.1 で書き出したバックアップが 0.3.0 で読めなくなるのはその通りです。そのうえで、今回は**現状のまま(封筒のないJSONは受け付けない)**で進めます。
理由は、0.2.1 の exportBackup が書き出す { activeNotebookId, notebooks } と Web版 (index.html) の書き出しが構造的に完全に同一で、両者を区別する手段が無いためです。旧形式を受け入れると、このPRの要件である「Web版とのインポート互換の打ち切り」が事実上撤回されます。二者択一になるため作者に確認し、互換打ち切りを優先する判断となりました。
移行手段は README に記載してあります(0.2.x で復元 → 0.3.0 で書き出し直す)。
Generated by Claude Code
| addShape(p.x, p.y); | ||
| const newId = addShape(p.x, p.y); | ||
| select(newId); | ||
| useUi.getState().setFocus(newId); |
There was a problem hiding this comment.
Consume the focus request for new text shapes
Pressing +テキスト stores the new shape ID in focusId, but ShapeCard never reads that state or applies autoFocus to its TextInput (only the flowchart components consume it). The new card is selected but the keyboard does not open and the user must tap the input again, so the focus-management path introduced here does not work for text shapes.
Useful? React with 👍 / 👎.
| minW={ARROW_MIN_LENGTH} | ||
| maxW={ARROW_MAX_LENGTH} | ||
| selected={selected} | ||
| bodyDraggable |
There was a problem hiding this comment.
Keep manually moved arrows within the scrollable canvas
Enabling body dragging allows an arrow to acquire an arbitrarily large y coordinate because Transformable enforces only a lower bound. FreeformCanvas calculates its height from shapes, photos, and stickers but not manual arrows, and absolute children do not enlarge layout, so dragging a line below the last content can place it beyond the ScrollView's reachable extent and leave no way to select, reset, or delete it; include manual arrows in the canvas extent or cap their vertical movement.
Useful? React with 👍 / 👎.
- Transformable: 中身をひとかたまりの読み上げ要素にまとめる挙動を bodyAccessible で切れるようにし、編集できる入力欄を持つテキストカードでは 無効化する。TalkBack から入力欄に降りられなくなっていた - Transformable: 位置と幅をキャンバス内へ丸めるかを bounded で切れるように し、矢印では無効化する。回転した矢印の「箱の左上」は画面上の位置と一致 しないため、紙の幅で丸めると離れた図形をつなぐ長い線が縮んで届かなくなっていた - FreeformCanvas: 紙の高さの計算に手動配置の矢印を含める。図形から離れた 位置へ動かした線が紙の外に出て、選択も削除もできなくなることがあった - ShapeCard: 「+テキスト」で置いたカードにフォーカスが移っていなかった (focusId を工程側しか読んでいなかった)。選択が乗ってから入力欄を focus する Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VhmTi5pjG9aQVypwr6qSGN
There was a problem hiding this comment.
Actionable comments posted: 5
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (2)
src/app/notebook/[id].tsx-188-192 (1)
188-192: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winSilent no-op when duplication is rejected.
Every other page action gives feedback (
onDeletePagetoasts on the last-page guard), but a falsyduplicatePageresult here returns with no user-visible signal.💡 Add a failure toast
const onDuplicatePage = (pageId: string) => { - if (!duplicatePage(pageId)) return; + if (!duplicatePage(pageId)) { + showToast("⚠️ ページをコピーできませんでした"); + return; + } tapFeedback(); showToast("ページをコピーしたよ📄✨"); };src/components/board/BoardTabs.tsx-151-158 (1)
151-158: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
borderRadius: 0won't flatten the tab corners.
styles.tabsets the four individual corner props (borderTopLeftRadiusetc.), and in React Native the specific corner properties win overborderRadiusirrespective of style-array order. Chic tabs will keep the 14/4 rounding.🎨 Proposed fix
tabChic: { backgroundColor: "transparent", - borderRadius: 0, + borderTopLeftRadius: 0, + borderTopRightRadius: 0, + borderBottomLeftRadius: 0, + borderBottomRightRadius: 0, borderWidth: 0,
🧹 Nitpick comments (5)
src/lib/model.test.ts (1)
680-690: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a photo-id-regenerated assertion for full duplication coverage.
This test checks id regeneration for
steps,stickers, andshapes, but not forphotos, even thoughduplicatePageregenerates photo ids too (photos: page.photos.map((p) => ({ ...p, id: newId() }))). OnlydataUrlretention is checked here.♻️ Suggested addition
expect(copy.photos[0]?.dataUrl).toBe("data:image/png;base64,AAAA"); + expect(copy.photos[0]?.id).not.toBe("ph1");src/components/ui/StickerShape.tsx (1)
21-162: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winSticker default colors duplicate
palettetokens instead of importing them.Several
c1/c2defaults here are literal copies ofpaletteentries insrc/lib/theme.ts(e.g."#F4D58D"=palette.gold,"#E8B4BC"=palette.rose,"#C9B6E4"=palette.lavender,"#A8E6CF"=palette.mint,"#FFFFFF"=palette.white). Thecrown(Line 114-115) andbutterfly(Line 132) draws also hardcode extra stroke colors ("#FFFFFF","#7A6A90") that never go throughc1/c2at all. This duplicates the exact palette this PR introduces to centralize color literals, so a future palette retune won't propagate here.♻️ Example fix (import and reuse palette tokens)
+import { palette } from "`@/lib/theme`"; + const STICKERS: Record<StickerType, StickerDef> = { star: { label: "星", - c1: "`#F4D58D`", - c2: "`#FFFFFF`", + c1: palette.gold, + c2: palette.white, ...src/components/flowchart/FlowchartSection.tsx (1)
22-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd
accessibilityValueto the progress bar.The
progressbarrole currently relies solely on the text label. Pairing it withaccessibilityValue={{ min: 0, max: total, now: done }}gives assistive tech a structured, standard way to report progress instead of parsing free text.♻️ Proposed addition
<View style={styles.progress} accessibilityRole="progressbar" accessibilityLabel={`${total}工程のうち${done}つ完了`} + accessibilityValue={{ min: 0, max: total, now: done }} >src/components/flowchart/IfCard.tsx (1)
120-161: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winBranch step completion misses haptic/confetti feedback.
BranchStepRow'sonPress={() => toggleStep(branch.id)}toggles completion but never callssuccessFeedback()or renders<Confetti/>, unlikeStepCard'sonCheck, which fires both on transition to done. This creates an inconsistent celebration experience depending on whether a step lives at the top level or inside an if-branch.♻️ Proposed fix to mirror StepCard's onCheck behavior
+import { successFeedback } from "`@/lib/haptics`"; +import { Confetti } from "./Confetti"; +import { useState } from "react"; ... function BranchStepRow({ branch, position }: { branch: NormalStep; position: Position }) { const setStepText = useNotebooks((s) => s.setStepText); const toggleStep = useNotebooks((s) => s.toggleStep); const deleteStep = useNotebooks((s) => s.deleteStep); const focusId = useUi((s) => s.focusId); const setFocus = useUi((s) => s.setFocus); + const [burst, setBurst] = useState(0); + + const onCheck = () => { + const nowDone = toggleStep(branch.id); + if (nowDone) { + setBurst((b) => b + 1); + successFeedback(); + } + }; return ( <View style={styles.branchStep}> <Pressable style={[styles.bcheck, branch.done && styles.bcheckDone]} - onPress={() => toggleStep(branch.id)} + onPress={onCheck} hitSlop={8} ... > ... </Pressable> ... + {burst > 0 ? <Confetti key={burst} /> : null} </View> ); }src/components/board/NoteControls.tsx (1)
70-79: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse a non-color indicator for the selected paper-color swatch.
swatchActiveonly changeschic.border(#DDDDDD) tochic.rule(#3A3A3A), so selection can be missed by users who cannot rely on a color-only state. Add a visible cue such as a thicker border, selected ring, or check mark in the selected style.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: QUIET
Plan: Pro Plus
Run ID: 4e32a87c-f968-4513-a94f-5f490ddcf700
📒 Files selected for processing (52)
README.mdapp.jsonpackage.jsonsrc/app/_layout.tsxsrc/app/index.tsxsrc/app/notebook/[id].tsxsrc/components/board/BoardFrame.tsxsrc/components/board/BoardTabs.tsxsrc/components/board/FrameSelect.tsxsrc/components/board/NoteControls.tsxsrc/components/board/PaperBackground.tsxsrc/components/board/StickerItem.tsxsrc/components/board/StickerTray.tsxsrc/components/cover/CoverArt.tsxsrc/components/cover/CoverGrid.tsxsrc/components/cover/CreatePanel.tsxsrc/components/flowchart/Confetti.tsxsrc/components/flowchart/FlowchartSection.tsxsrc/components/flowchart/IfCard.tsxsrc/components/flowchart/StepActions.tsxsrc/components/flowchart/StepCard.tsxsrc/components/freeform/ArrowLayer.tsxsrc/components/freeform/FreeformCanvas.tsxsrc/components/freeform/PhotoCard.tsxsrc/components/freeform/ShapeCard.tsxsrc/components/notebook/NotebookSection.tsxsrc/components/transform/Transformable.tsxsrc/components/ui/Background.tsxsrc/components/ui/Gradient.tsxsrc/components/ui/PromptModal.tsxsrc/components/ui/SparkleLayer.tsxsrc/components/ui/StickerShape.tsxsrc/components/ui/Toast.tsxsrc/components/ui/WashiTape.tsxsrc/components/ui/kit.tsxsrc/lib/backup.tssrc/lib/backupFormat.test.tssrc/lib/backupFormat.tssrc/lib/capture.tssrc/lib/color.test.tssrc/lib/color.tssrc/lib/format.test.tssrc/lib/format.tssrc/lib/haptics.tssrc/lib/model.test.tssrc/lib/model.tssrc/lib/theme.tssrc/lib/types.tssrc/state/backup.tssrc/state/notebooks.tssrc/state/ui.tsvitest.config.ts
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4a46789dc9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| handleTint={chic.handle} | ||
| // 矢印は回転させて使うので、箱の左上はキャンバス上の位置と一致しない。 | ||
| // ここを紙の幅で丸めると、離れた図形をつなぐ長い線が縮んで届かなくなる。 | ||
| bounded={false} |
There was a problem hiding this comment.
Keep dragged arrows within a recoverable area
When a selected arrow is dragged far enough toward the top or either horizontal edge, bounded={false} permits its center—and therefore the entire visible rotated line—to leave the canvas. The resulting coordinates are persisted by updateArrow, while the canvas provides neither upward nor horizontal scrolling, so the arrow can no longer be selected to reset or delete it; preserve the rotation-aware sizing fix while constraining the arrow's visible center or retaining a reachable portion.
Useful? React with 👍 / 👎.
- Transformable: 「↺ 自動の位置に戻す」ボタンが回転つまみと同じ左上角に
重なり、手動配置した矢印を回せなくなっていた。空いている左下へ移動
- ArrowLayer: bounded={false} にしたことで、矢印を紙の外まで動かせて
しまい、選び直しも削除もできなくなる余地が残っていた。箱の左上ではなく
「掴める中心」を紙の中に丸める(回転した線が紙からはみ出すのは正常なので、
そこは丸めない)
- BoardTabs: タブの外枠を押せない容れ物にして、ページ切り替えとコピー・削除
を兄弟に並べた。押せる要素の入れ子は TalkBack が外側だけをひとかたまりで
拾い、中のボタンに降りられなくなるため
- BoardTabs: ノート式の平らなタブに borderRadius:0 が効いていなかった
(React Native では個別の角指定が shorthand より優先される)
- Toast: 読み上げ領域ごと出し入れしていたため、知らせが読み上げられない
ことがあった。入れ物は出したままにして中身だけ差し替える
- capture: 同じページを同じ日に2回保存すると同名になり、前回の共有がまだ
開いているかもしれないファイルを消していた。分まで入れたうえで、既存
ファイルには触れず一時ファイルをそのまま共有する
- NoteControls: 用紙色の選択が枠の色だけの違いだったので、チェックを重ねて
形でも分かるようにした
- IfCard: 分岐の中の工程にチェックを付けたときの手ごたえが無かった
- FlowchartSection: 進み具合バーに accessibilityValue を付与
- 未計測テキストカードの高さの見積もりが2か所で食い違っていた(120と44)
ので UNMEASURED_SHAPE_HEIGHT に集約
- StickerShape: シールの既定色をパレットのトークン参照に置き換え
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VhmTi5pjG9aQVypwr6qSGN
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 993a0f4ddf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| <Pressable | ||
| style={({ pressed }) => [styles.card, pressed && styles.cardPressed]} | ||
| onPress={onOpen} | ||
| accessibilityRole="button" | ||
| accessibilityLabel={`${name}を開く`} |
There was a problem hiding this comment.
Keep cover actions outside the accessible opener
With TalkBack enabled, this outer Pressable becomes one accessibility element labeled “...を開く” and groups the nested IconButton controls, so users cannot focus the rename, copy, or delete actions independently. Use a non-accessible card wrapper and make the opener and action buttons siblings, as BoardTabs does for the same Android behavior.
Useful? React with 👍 / 👎.
| const name = notebookDisplayName(notebook); | ||
|
|
||
| return ( | ||
| <Animated.View style={styles.cell} entering={FadeInDown.delay(index * 45).duration(260)}> |
There was a problem hiding this comment.
Cap the staggered cover animation delay
When a backup or long-lived installation contains many notebooks, multiplying every index by 45 ms makes later covers—and the add card, which uses the total notebook count—remain in their delayed entering state for several seconds (100 notebooks means about 4.5 seconds). Because notebook count is unbounded, cap the stagger index so large collections become usable immediately.
Useful? React with 👍 / 👎.
- CoverGrid: カードの外枠を押せない容れ物にして、「開く」と名前の変更・ コピー・削除を兄弟に並べた。押せる要素の入れ子は TalkBack が外側だけを ひとかたまりで拾い、中のボタンに降りられなくなるため(BoardTabs で直した のと同じ構造の見落とし) - CoverGrid: 表紙が順に浮かび上がる演出のずらし段数に上限を設けた。冊数に 比例して遅らせていたため、冊数が多いと最後のカードと「新しい手帳」タイル が数秒間ずっと出てこなかった(100冊で約4.5秒) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VhmTi5pjG9aQVypwr6qSGN
Summary
This release adds new sticker types, implements page/notebook duplication, improves accessibility, refactors theme constants, and enhances the overall UI with better visual feedback and component organization.
Key Changes
New Features
duplicatePage()function to clone pages with all their content (steps, stickers, shapes, etc.)duplicateNotebook()to duplicate entire notebooks with all pagesUI & Accessibility Improvements
palette,colors,text,space,radii,shadows)IconButton,Chip,PanelTitle,GradientFill,WashiTape) for consistent stylingCode Organization
color.tsmodule with functions for color manipulation (mix, lighten, darken, luminance detection)backupFormat.tswith versioned backup envelope structure to support future format changesmoveStepInTree(),copyName(),duplicatePage(),duplicateNotebook(), andstepProgress()functionshaptics.tsmodule for safe haptic feedback (success, selection, impact)StepActionscomponentData Model Changes
moveStep()to accept step ID instead of index for more reliable operationsBug Fixes & Refinements
findStepInTree()function in favor of direct tree operationsTesting
color.test.ts)backupFormat.test.ts)Version & Dependencies
https://claude.ai/code/session_01VhmTi5pjG9aQVypwr6qSGN