Skip to content

描画エンジンをsrc/drawing-engine/に分離 + 頂点削除機能を追加#50

Merged
sugama-satsuki merged 3 commits intomainfrom
refactor/drawing-engine
Mar 2, 2026
Merged

描画エンジンをsrc/drawing-engine/に分離 + 頂点削除機能を追加#50
sugama-satsuki merged 3 commits intomainfrom
refactor/drawing-engine

Conversation

@sugama-satsuki
Copy link
Collaborator

@sugama-satsuki sugama-satsuki commented Feb 24, 2026

Summary

  • 描画関連のフック・コンポーネント・ユーティリティを src/drawing-engine/ に分離し、npmパッケージ化可能な構成に整理
  • useDrawingEngine フックで描画エンジンの全機能を一括提供(モード切替、Undo/Redo、頂点編集、インポートなど)
  • 頂点削除機能を追加: ポリゴンやラインの特定の頂点を削除可能に
    • 右クリックメニュー(「この頂点を削除」)
    • クリック選択 + Delete/Backspace キー
    • 選択中の頂点は赤くハイライト表示(data-driven styling)
    • 最小頂点数チェック(LineString: 2, Polygon: 3)で削除不可の場合は disabled
    • ポリゴンのリング閉じ処理を維持
    • Undo/Redo 対応

影響範囲

  • src/components/MapView.tsx: useDrawingEngine フックを利用するように大幅簡素化
  • src/drawing-engine/: 新規ディレクトリ(hooks, components, lib, types を格納)
  • E2E テスト: 頂点削除シナリオ 5 件追加
  • ユニットテスト: vertex-helpers.test.ts 12 テストケース追加
  • 既存の描画・編集・選択・Undo/Redo 機能に影響なし(全 E2E テスト通過)

変更ファイル一覧

新規作成

  • src/drawing-engine/ ディレクトリ一式(hooks, components, lib, types, index.ts, CSS)
  • src/drawing-engine/lib/vertex-helpers.tscanDeleteVertex / applyVertexDelete
  • src/drawing-engine/components/VertexContextMenu.tsx + CSS
  • e2e/features/vertex-deletion.feature — 5 シナリオ
  • e2e/step-definitions/vertex-deletion-steps.ts

変更

  • src/components/MapView.tsx — drawing-engine フック利用に簡素化 + VertexContextMenu 接続
  • e2e/support/helpers/selectors.ts — 頂点コンテキストメニューセレクタ追加

Test plan

  • npm test — ユニットテスト 98 件全パス
  • npm run test:e2e — E2E テスト 57 シナリオ / 366 ステップ全パス
  • npm run build — ビルド成功
  • ローカルで以下を目視確認:
    • ラインを選択 → 頂点をクリック → 赤くハイライト → Delete キーで削除
    • ポリゴン頂点を右クリック → メニュー表示 → 「この頂点を削除」で削除
    • 最小頂点数の場合は削除が無効化される
    • Ctrl+Z で削除を元に戻せる

MapView.tsx(590行)に密結合していた描画ロジックを src/drawing-engine/ として
モジュール化。将来的に別リポジトリ・npmパッケージとして切り出せる形にした。

- useDrawingEngine フック新規作成: レイヤー管理・イベント処理・状態管理を統合
- コンポーネント移動: DrawControlPanel, DrawModeSelector, DrawModeIcons + CSS
- Hooks 移動: useUndoable, useVertexEditing
- ユーティリティ移動: geojson-helpers, csv-helpers, clamp-position + テスト
- CSS変数を --de-* プレフィックスで名前空間化しフォールバック付与
- types.ts で DrawMode/PathMode を一元管理し循環依存を解消
- barrel export (index.ts) で公開API を整理
- MapView.tsx を約130行に簡素化(アプリ固有ロジックのみ残存)
@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

📝 Walkthrough

Walkthrough

MapViewの描画・編集ロジックを新しいuseDrawingEngineフックへ移譲し、頂点編集、undo/redo、CSV/GeoJSONインポート、ラバーバンド選択、コンテキストメニュー等の状態・レイヤ管理をdrawing-engineに集約。CSSトークンと型定義も整理。

Changes

Cohort / File(s) Summary
Drawing Engine: core hook & API
src/drawing-engine/hooks/useDrawingEngine.ts, src/drawing-engine/index.ts, src/drawing-engine/types.ts
新規フックexport useDrawingEngine と公開型(DrawingEngineOptions/Result、ContextMenuEvent等)を追加。エンジンがfeatures、undo/redo、インポート、ラバーバンド、controlPanelProps等を公開。barrelにVertexContextMenuとvertexヘルパーを追加。
Vertex editing & helpers
src/drawing-engine/hooks/useVertexEditing.ts, src/drawing-engine/lib/vertex-helpers.ts, src/drawing-engine/lib/__tests__/vertex-helpers.test.ts
頂点選択/コンテキストメニュー/削除のイベント追加、deleteSelectedVertex API、canDeleteVertex/applyVertexDeleteユーティリティ追加とユニットテスト。
MapView integration
src/components/MapView.tsx, src/components/GeoJSONPanel.tsx
ローカルの描画/編集状態・ハンドラを削除し、engine経由(engine.features, engine.controlPanelProps, engine.contextMenuEvent, engine.rubberBand, engine.vertexContextMenuEvent 等)でUIと連携するように変更。
UI components & new context menu
src/drawing-engine/components/DrawControlPanel.tsx, src/drawing-engine/components/DrawModeSelector.tsx, src/drawing-engine/components/VertexContextMenu.tsx, src/drawing-engine/components/DrawModeIcons.tsx
DrawControlPanelPropsをエクスポート化、DrawMode型をcentral typesへ移動、VertexContextMenuコンポーネント追加(頂点削除アクション)。
Styling / tokens
src/drawing-engine/components/DrawControlPanel.css, src/drawing-engine/components/DrawModeSelector.css, src/drawing-engine/components/VertexContextMenu.css, src/drawing-engine/drawing-engine.css
CSSカスタムプロパティを--de-プレフィックスに統一、フォールバック値を追加。VertexContextMenu用スタイルを追加。drawing-engineレイヤでトークン定義。
Types & minor lib changes
src/drawing-engine/lib/geojson-helpers.ts
内部でのDrawMode/PathMode型参照をcentral typesへ切替。
Tests / e2e
e2e/features/vertex-deletion.feature, e2e/step-definitions/vertex-deletion-steps.ts, e2e/support/helpers/selectors.ts
頂点削除のE2Eシナリオとステップ定義を追加。selectorに頂点コンテキストメニュー定数を追加。
Tooling
vite.config.js
テストカバレッジ設定にdrawing-engine配下を追加。
sequenceDiagram
    participant User
    participant MapView
    participant Engine as Drawing Engine Hook
    participant Map as MapLibre
    participant UI as DrawControlPanel / GeoJSONPanel

    User->>MapView: マップ初期化 (initialFeatures)
    MapView->>Engine: useDrawingEngine(map, { initialFeatures })
    Engine->>Map: ソース/レイヤを作成・登録
    Engine-->>MapView: engine オブジェクト返却

    User->>UI: モード選択・ボタンクリック
    UI-->>MapView: controlPanelProps 経由でアクション発行
    MapView->>Engine: engine.setDrawMode / engine.* アクション

    User->>Map: クリック/ドラッグ/右クリック
    Map-->>Engine: map イベント伝播
    Engine->>Engine: features / draft / selection 更新
    Engine->>Map: feature source 同期
    Engine-->>MapView: 更新された features, rubberBand, contextMenuEvent を通知

    User->>MapView: Ctrl+Z / Delete / コンテキスト操作
    MapView->>Engine: engine.undo() / engine.deleteSelectedVertex() / engine.closeContextMenu()
    Engine->>Map: state 同期更新
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰✨ 新しいエンジンが跳ねる地図の朝
点と線はひとつの声で歌いだす
頂点はそっと選ばれ、消え、もどる
MapViewは軽やか、状態はエンジンにおまかせ
さあ、ぴょんと描こう — 地図の上で 🗺️

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed タイトルはPRの主要な変更内容を正確に反映しており、描画エンジンの分離と頂点削除機能の追加という2つの重要な変更を簡潔に表現している。

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor/drawing-engine

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (6)
src/drawing-engine/hooks/useDrawingEngine.ts (2)

349-357: forEach のコールバックが Set.add() の戻り値を暗黙的に返しています。

Biome の useIterableCallbackReturn ルールが指摘する通り、forEach コールバックは値を返すべきではありません。Set.add()Set 自身を返すため、意図しない戻り値が生じています。実害はありませんがリンターを満たすためにブロック文にできます。

♻️ 修正案
           const next = new Set(prev)
-          ids.forEach((id) => next.add(id))
+          for (const id of ids) next.add(id)
           return next
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/hooks/useDrawingEngine.ts` around lines 349 - 357, The
forEach callback in the setSelectedFeatureIds update implicitly returns the
result of Set.add(), which violates the useIterableCallbackReturn rule; update
the callback used on ids inside the shiftKey branch (where ids is a Set and next
is a new Set(prev)) to use a block-bodied callback or a for..of loop so it does
not return a value (e.g., replace ids.forEach((id) => next.add(id)) with a block
callback like ids.forEach((id) => { next.add(id) }) or iterate with for (const
id of ids) next.add(id) within the setSelectedFeatureIds handler).

71-72: emptyFC をモジュールレベルの定数に昇格させることを検討してください。

emptyFC はレンダーごとに新しいオブジェクトが生成されます。useEffect 内で setData に渡される際にも毎回新しい参照が使われます。モジュールスコープの定数にすれば不要なオブジェクト生成を回避できます。

♻️ 提案
+const EMPTY_FC: GeoJSON.FeatureCollection = { type: 'FeatureCollection', features: [] }
+
 export function useDrawingEngine(
   map: maplibregl.Map | null,
   options?: DrawingEngineOptions,
 ): DrawingEngineResult {
-  const emptyFC: GeoJSON.FeatureCollection = { type: 'FeatureCollection', features: [] }
-  const initialFC = options?.initialFeatures ?? emptyFC
+  const initialFC = options?.initialFeatures ?? EMPTY_FC

フック内で emptyFC を参照している箇所も EMPTY_FC に置き換えてください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/hooks/useDrawingEngine.ts` around lines 71 - 72, emptyFC
is recreated on every render causing new references to be passed into initialFC
and any setData calls; lift it to a module-level constant (e.g. export const
EMPTY_FC = { type: 'FeatureCollection', features: [] } as
GeoJSON.FeatureCollection) and replace all local uses of emptyFC and the
fallback initialFC with EMPTY_FC (update the declaration that builds initialFC
and any places that pass emptyFC into setData or other functions so they use the
shared EMPTY_FC reference).
src/components/MapView.tsx (2)

55-59: navigator.clipboard.writeText の Promise rejection ハンドリングが不完全です。

Line 57-59 で .catch はありますが、HTTP 以外のコンテキスト(例: file://)や Permissions API で拒否された場合、writeText 自体が undefined になる可能性があります。既存コードではありますが、独立モジュール化を機にフォールバック(document.execCommand('copy') 等)の追加を検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/MapView.tsx` around lines 55 - 59,
navigator.clipboard.writeText may be unavailable or undefined in some contexts
(e.g., file:// or denied by Permissions API); update the MapView copy flow to
guard for navigator.clipboard and provide a fallback copy method: if
navigator.clipboard?.writeText exists use it and handle its Promise, otherwise
fall back to a synchronous copy via a temporary textarea +
document.execCommand('copy') (or use the Permissions API to attempt/request
permission first), and ensure both paths call handleCopy with success/error
messages referencing the same url variable; adjust the copy callsite in MapView
and keep handleCopy usage intact.

62-78: engine.features を依存配列に含む useCallback はフィーチャ変更のたびに再生成されます。

handlePanelFeatureClick(Line 72)は engine.features に依存しているため、フィーチャが変更されるたびにコールバックが再生成され、GeoJSONPanel の不要な再レンダーを引き起こす可能性があります。

engine.setFeatures のように、engine 側でフィーチャ一覧を受け取る flyToFeature(featureId) のようなアクションを提供するか、engine.features を ref 経由で参照すればコールバックを安定化できます。パフォーマンスが問題にならない規模であれば現状で問題ありません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/MapView.tsx` around lines 62 - 78, handlePanelFeatureClick
re-creates on every change because it depends on engine.features; to stabilize
it, remove engine.features from the dependency array and read features via a
stable accessor instead—either add a method on engine (e.g.,
engine.getFeatureById or engine.flyToFeature(featureId)) and call that inside
handlePanelFeatureClick, or store engine.features in a ref (useRef) and read
ref.current inside the callback; ensure the dependency array only contains
stable references (map and engine.setSelectedFeatureIds or engine itself if
stable) and keep using getFeatureCenter and engine.setSelectedFeatureIds as
before.
src/drawing-engine/components/DrawControlPanel.tsx (1)

128-130: GeoJSON インポート失敗時のユーザーフィードバックが不足しています。

console.error のみでユーザーにはエラーが表示されません。既存動作ではありますが、このコンポーネントが独立モジュールとして切り出される以上、onImportGeoJSON と同様にエラーコールバック(例: onError)を props に追加し、ホストアプリ側でトースト等を表示できるようにすることを検討してください。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/components/DrawControlPanel.tsx` around lines 128 - 130,
The catch block in DrawControlPanel.tsx only calls console.error and provides no
user-facing feedback; add an onError prop to the DrawControlPanel component
(update the component's props/interface and default to a no-op) and invoke
onError(err, 'Failed to import GeoJSON') inside the catch alongside the existing
console.error so host apps can show toasts or handle errors; ensure the prop is
documented and used similarly to onImportGeoJSON (pass the actual error object
and a concise message).
src/drawing-engine/index.ts (1)

1-31: Barrel export の構成は明確で、セクション分けも適切です。

将来の npm パッケージ化を見据えた公開 API サーフェスとして整理されています。

一点検討事項として、useUndoableuseVertexEditingnextFeatureIdclosePolygonRingclampPosition などの内部ヘルパーも公開されています。npm パッケージ化の際、意図しない破壊的変更を避けるため、コンシューマ向け API と内部ユーティリティを分離する(例: サブパス drawing-engine/internals からの export)ことを将来的に検討してもよいかもしれません。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/index.ts` around lines 1 - 31, The barrel currently
exposes internal helpers that should not be part of the public API; move the
internal exports (useUndoable, useVertexEditing, nextFeatureId,
closePolygonRing, clampPosition and similar helpers) out of the main index and
into a dedicated internal barrel (e.g., an "internals" export file or subpath
like drawing-engine/internals), then update src/drawing-engine/index.ts to only
export the public components/hooks/types (DrawControlPanel, DrawModeSelector,
useDrawingEngine,
createPointFeature/createPathFeature/createDraftFeatureCollection/parseGeoJSONImport,
parseCSV, and types) so consumers only get the intended public surface while
internal utilities remain available via the new internal entry point.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/drawing-engine/components/DrawControlPanel.css`:
- Around line 14-17: Remove the empty line(s) that precede CSS declarations so
Stylelint's declaration-empty-line-before rule is satisfied: delete the blank
line between the -webkit-backdrop-filter declaration and the border declaration
(the blank before the border rule), and likewise remove the blank line that
appears immediately before the transition declaration; this keeps the
backdrop-filter, -webkit-backdrop-filter, border, and transition declarations
contiguous.

---

Nitpick comments:
In `@src/components/MapView.tsx`:
- Around line 55-59: navigator.clipboard.writeText may be unavailable or
undefined in some contexts (e.g., file:// or denied by Permissions API); update
the MapView copy flow to guard for navigator.clipboard and provide a fallback
copy method: if navigator.clipboard?.writeText exists use it and handle its
Promise, otherwise fall back to a synchronous copy via a temporary textarea +
document.execCommand('copy') (or use the Permissions API to attempt/request
permission first), and ensure both paths call handleCopy with success/error
messages referencing the same url variable; adjust the copy callsite in MapView
and keep handleCopy usage intact.
- Around line 62-78: handlePanelFeatureClick re-creates on every change because
it depends on engine.features; to stabilize it, remove engine.features from the
dependency array and read features via a stable accessor instead—either add a
method on engine (e.g., engine.getFeatureById or engine.flyToFeature(featureId))
and call that inside handlePanelFeatureClick, or store engine.features in a ref
(useRef) and read ref.current inside the callback; ensure the dependency array
only contains stable references (map and engine.setSelectedFeatureIds or engine
itself if stable) and keep using getFeatureCenter and
engine.setSelectedFeatureIds as before.

In `@src/drawing-engine/components/DrawControlPanel.tsx`:
- Around line 128-130: The catch block in DrawControlPanel.tsx only calls
console.error and provides no user-facing feedback; add an onError prop to the
DrawControlPanel component (update the component's props/interface and default
to a no-op) and invoke onError(err, 'Failed to import GeoJSON') inside the catch
alongside the existing console.error so host apps can show toasts or handle
errors; ensure the prop is documented and used similarly to onImportGeoJSON
(pass the actual error object and a concise message).

In `@src/drawing-engine/hooks/useDrawingEngine.ts`:
- Around line 349-357: The forEach callback in the setSelectedFeatureIds update
implicitly returns the result of Set.add(), which violates the
useIterableCallbackReturn rule; update the callback used on ids inside the
shiftKey branch (where ids is a Set and next is a new Set(prev)) to use a
block-bodied callback or a for..of loop so it does not return a value (e.g.,
replace ids.forEach((id) => next.add(id)) with a block callback like
ids.forEach((id) => { next.add(id) }) or iterate with for (const id of ids)
next.add(id) within the setSelectedFeatureIds handler).
- Around line 71-72: emptyFC is recreated on every render causing new references
to be passed into initialFC and any setData calls; lift it to a module-level
constant (e.g. export const EMPTY_FC = { type: 'FeatureCollection', features: []
} as GeoJSON.FeatureCollection) and replace all local uses of emptyFC and the
fallback initialFC with EMPTY_FC (update the declaration that builds initialFC
and any places that pass emptyFC into setData or other functions so they use the
shared EMPTY_FC reference).

In `@src/drawing-engine/index.ts`:
- Around line 1-31: The barrel currently exposes internal helpers that should
not be part of the public API; move the internal exports (useUndoable,
useVertexEditing, nextFeatureId, closePolygonRing, clampPosition and similar
helpers) out of the main index and into a dedicated internal barrel (e.g., an
"internals" export file or subpath like drawing-engine/internals), then update
src/drawing-engine/index.ts to only export the public components/hooks/types
(DrawControlPanel, DrawModeSelector, useDrawingEngine,
createPointFeature/createPathFeature/createDraftFeatureCollection/parseGeoJSONImport,
parseCSV, and types) so consumers only get the intended public surface while
internal utilities remain available via the new internal entry point.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f550345 and 141f868.

📒 Files selected for processing (20)
  • src/components/MapView.tsx
  • src/drawing-engine/components/DrawControlPanel.css
  • src/drawing-engine/components/DrawControlPanel.tsx
  • src/drawing-engine/components/DrawModeIcons.tsx
  • src/drawing-engine/components/DrawModeSelector.css
  • src/drawing-engine/components/DrawModeSelector.tsx
  • src/drawing-engine/drawing-engine.css
  • src/drawing-engine/hooks/useDrawingEngine.ts
  • src/drawing-engine/hooks/useUndoable.ts
  • src/drawing-engine/hooks/useVertexEditing.ts
  • src/drawing-engine/index.ts
  • src/drawing-engine/lib/__tests__/clamp-position.test.ts
  • src/drawing-engine/lib/__tests__/csv-helpers.test.ts
  • src/drawing-engine/lib/__tests__/geojson-helpers.test.ts
  • src/drawing-engine/lib/__tests__/useUndoable.test.ts
  • src/drawing-engine/lib/clamp-position.ts
  • src/drawing-engine/lib/csv-helpers.ts
  • src/drawing-engine/lib/geojson-helpers.ts
  • src/drawing-engine/types.ts
  • vite.config.js

Comment on lines +14 to +17
backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
-webkit-backdrop-filter: blur(var(--de-glass-blur-strong, 24px));

border: 1px solid var(--glass-border);
border: 1px solid var(--de-glass-border, rgba(255, 255, 255, 0.45));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stylelint: 宣言前の空行を削除してください。

Stylelint (declaration-empty-line-before) が Line 17 の border 宣言の前の空行をエラーとして検出しています。Line 31 の transition 宣言前にも同様のエラーがあります。

🔧 修正案

Line 16 の空行を削除:

   backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
   -webkit-backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
-
   border: 1px solid var(--de-glass-border, rgba(255, 255, 255, 0.45));

Line 30 付近の空行も同様に削除してください(Line 31 の transition 宣言前)。

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
-webkit-backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
border: 1px solid var(--glass-border);
border: 1px solid var(--de-glass-border, rgba(255, 255, 255, 0.45));
backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
-webkit-backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
border: 1px solid var(--de-glass-border, rgba(255, 255, 255, 0.45));
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 17-17: Unexpected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/components/DrawControlPanel.css` around lines 14 - 17,
Remove the empty line(s) that precede CSS declarations so Stylelint's
declaration-empty-line-before rule is satisfied: delete the blank line between
the -webkit-backdrop-filter declaration and the border declaration (the blank
before the border rule), and likewise remove the blank line that appears
immediately before the transition declaration; this keeps the backdrop-filter,
-webkit-backdrop-filter, border, and transition declarations contiguous.

- MapView.tsx: drawing-engineアーキテクチャ維持しつつHelpSidebar・GeoJSONPanelへのimport/share propsを取り込み
- DrawControlPanel.css: 不要になったシェア・インポート関連CSSを削除
- useDrawingEngine.ts: controlPanelPropsからimport/share関連を除外し型を整理
- GeoJSONPanel.tsx: parseGeoJSONImportのインポートパスをdrawing-engineに修正
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
src/drawing-engine/components/DrawControlPanel.css (1)

14-31: ⚠️ Potential issue | 🟡 Minor

Stylelintエラー解消のため、宣言前の空行を削除してください。

Line 17 と Line 31 の直前にある空行が declaration-empty-line-before 違反です。

🔧 修正例
   backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
   -webkit-backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
-
   border: 1px solid var(--de-glass-border, rgba(255, 255, 255, 0.45));
@@
   z-index: 2;
   user-select: none;
-
   transition: box-shadow var(--de-transition-slow, 0.4s cubic-bezier(0.4, 0, 0.2, 1));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/components/DrawControlPanel.css` around lines 14 - 31,
Remove the empty lines that appear immediately before the CSS declarations
causing the declaration-empty-line-before rule to fail: delete the blank line
before the "border: 1px solid var(--de-glass-border...)" declaration and the
blank line before the "transition: box-shadow var(--de-transition-slow...)"
declaration in DrawControlPanel.css so there are no blank lines between adjacent
property declarations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/drawing-engine/hooks/useDrawingEngine.ts`:
- Around line 427-467: After deleting, resetting or replacing features you must
also close the context menu; update deleteSelectedFeatures, resetGeoJSON, and
the 'replace' branch inside importGeoJSON to clear the context menu state (call
the same state updater used for contextMenu, e.g. setContextMenu(null) or
equivalent) immediately after the feature/state changes and any highlightTimer
cleanup so the menu cannot remain open for features that no longer exist.
- Around line 350-354: In setSelectedFeatureIds inside useDrawingEngine.ts
update the shiftKey branch so the forEach callback does not implicitly return a
value; change the ids.forEach((id) => next.add(id)) usage to a block callback
that calls next.add(id) without returning (e.g., ids.forEach((id) => {
next.add(id); }) ) or use a for...of loop over ids to add each id to next,
keeping the created Set named next and returning it from the updater.

---

Duplicate comments:
In `@src/drawing-engine/components/DrawControlPanel.css`:
- Around line 14-31: Remove the empty lines that appear immediately before the
CSS declarations causing the declaration-empty-line-before rule to fail: delete
the blank line before the "border: 1px solid var(--de-glass-border...)"
declaration and the blank line before the "transition: box-shadow
var(--de-transition-slow...)" declaration in DrawControlPanel.css so there are
no blank lines between adjacent property declarations.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 141f868 and 2246d15.

📒 Files selected for processing (5)
  • src/components/GeoJSONPanel.tsx
  • src/components/MapView.tsx
  • src/drawing-engine/components/DrawControlPanel.css
  • src/drawing-engine/components/DrawControlPanel.tsx
  • src/drawing-engine/hooks/useDrawingEngine.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/drawing-engine/components/DrawControlPanel.tsx

Comment on lines +350 to +354
setSelectedFeatureIds((prev) => {
if (e.originalEvent.shiftKey) {
const next = new Set(prev)
ids.forEach((id) => next.add(id))
return next
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

git ls-files src/drawing-engine/hooks/useDrawingEngine.ts

Repository: geolonia/geojson-maker

Length of output: 110


🏁 Script executed:

wc -l src/drawing-engine/hooks/useDrawingEngine.ts

Repository: geolonia/geojson-maker

Length of output: 114


🏁 Script executed:

sed -n '345,360p' src/drawing-engine/hooks/useDrawingEngine.ts

Repository: geolonia/geojson-maker

Length of output: 744


🏁 Script executed:

find . -name "biome.json" -o -name "biome.jsonc" -o -name ".biomeignore" | head -20

Repository: geolonia/geojson-maker

Length of output: 78


🏁 Script executed:

cat biome.json

Repository: geolonia/geojson-maker

Length of output: 1840


🏁 Script executed:

cd src/drawing-engine/hooks && git diff HEAD useDrawingEngine.ts | head -100

Repository: geolonia/geojson-maker

Length of output: 48


🏁 Script executed:

git log --oneline -1 src/drawing-engine/hooks/useDrawingEngine.ts

Repository: geolonia/geojson-maker

Length of output: 117


🏁 Script executed:

rg -n "forEach\s*\(\s*\(" src/drawing-engine/hooks/useDrawingEngine.ts

Repository: geolonia/geojson-maker

Length of output: 214


🏁 Script executed:

# Check if there are other similar patterns in the codebase
rg -n "\.forEach\(\([^)]+\)\s*=>\s*[^{}]" --type ts --type tsx | head -20

Repository: geolonia/geojson-maker

Length of output: 93


🌐 Web query:

Biome useIterableCallbackReturn rule forEach implicit return

💡 Result:

Biome’s lint/suspicious/useIterableCallbackReturn enforces that iterable callbacks either always return a value (for methods like map, filter, find, etc.) or never return a value—and forEach is in the “must not return a value” category. If your forEach callback has an implicit return (e.g. an expression-bodied arrow), the rule can flag it as “should not return a value.” [1]

Fixes for forEach “implicit return”

Prefer a block body (no returned expression):

arr.forEach((x) => { doSideEffect(x); });

Or explicitly void the expression (allowed by the rule):

arr.forEach((x) => void doSideEffect(x));

(void return values don’t trigger the rule.) [1]

If you want Biome to stop checking forEach returns

Biome added an option in v2.4.0:

{
  "linter": {
    "rules": {
      "suspicious": {
        "useIterableCallbackReturn": {
          "options": { "checkForEach": false }
        }
      }
    }
  }
}

This skips forEach() callbacks that return a value. [1][2]

Sources: Biome rule docs and option checkForEach [1], Biome v2.4.0 release notes mentioning checkForEach support [2].

[1] (next.biomejs.dev)
[2] (newreleases.io)

Citations:


forEach コールバックで値を返さない形にしてください。

Line 353 の ids.forEach((id) => next.add(id))next.add(id) の戻り値を暗黙返却するため、Biome ルール useIterableCallbackReturn に違反します。

修正例
-          ids.forEach((id) => next.add(id))
+          ids.forEach((id) => {
+            next.add(id)
+          })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setSelectedFeatureIds((prev) => {
if (e.originalEvent.shiftKey) {
const next = new Set(prev)
ids.forEach((id) => next.add(id))
return next
setSelectedFeatureIds((prev) => {
if (e.originalEvent.shiftKey) {
const next = new Set(prev)
ids.forEach((id) => {
next.add(id)
})
return next
🧰 Tools
🪛 Biome (2.4.4)

[error] 353-353: This callback passed to forEach() iterable method should not return a value.

(lint/suspicious/useIterableCallbackReturn)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/hooks/useDrawingEngine.ts` around lines 350 - 354, In
setSelectedFeatureIds inside useDrawingEngine.ts update the shiftKey branch so
the forEach callback does not implicitly return a value; change the
ids.forEach((id) => next.add(id)) usage to a block callback that calls
next.add(id) without returning (e.g., ids.forEach((id) => { next.add(id); }) )
or use a for...of loop over ids to add each id to next, keeping the created Set
named next and returning it from the updater.

Comment on lines +427 to +467
const deleteSelectedFeatures = useCallback(() => {
if (selectedFeatureIds.size === 0) return
setFeatures((prev) => ({
...prev,
features: prev.features.filter((f) => !selectedFeatureIds.has(f.properties?._id as string)),
}))
setSelectedFeatureIds(new Set())
}, [selectedFeatureIds])

const resetGeoJSON = useCallback(() => {
if (highlightTimerRef.current) {
clearTimeout(highlightTimerRef.current)
highlightTimerRef.current = null
}
setFeatures({ type: 'FeatureCollection', features: [] })
setDraftCoords([])
setSelectedFeatureIds(new Set())
setHighlightedPanelFeatureId(null)
}, [])

const importCSV = useCallback((text: string) => {
const rows = parseCSV(text)
const newFeatures: GeoJSON.Feature[] = rows.map((row) => ({
type: 'Feature',
geometry: { type: 'Point', coordinates: [row.lng, row.lat] },
properties: { _id: nextFeatureId(), drawMode: 'point', ...row.properties },
}))
setFeatures((prev) => ({ ...prev, features: [...prev.features, ...newFeatures] }))
}, [])

const importGeoJSON = useCallback((importedFeatures: GeoJSON.Feature[], mode: 'replace' | 'merge') => {
if (mode === 'replace') {
if (highlightTimerRef.current) {
clearTimeout(highlightTimerRef.current)
highlightTimerRef.current = null
}
setFeatures({ type: 'FeatureCollection', features: importedFeatures })
setDraftCoords([])
setSelectedFeatureIds(new Set())
setHighlightedPanelFeatureId(null)
} else {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

削除/リセット/置換時にコンテキストメニューも閉じてください。

Line 427-467 の処理後、contextMenu が残ると削除済みフィーチャのメニューが表示され続ける可能性があります。

🔧 修正例
   const deleteSelectedFeatures = useCallback(() => {
     if (selectedFeatureIds.size === 0) return
     setFeatures((prev) => ({
       ...prev,
       features: prev.features.filter((f) => !selectedFeatureIds.has(f.properties?._id as string)),
     }))
     setSelectedFeatureIds(new Set())
+    setContextMenu(null)
   }, [selectedFeatureIds])

   const resetGeoJSON = useCallback(() => {
@@
     setFeatures({ type: 'FeatureCollection', features: [] })
     setDraftCoords([])
     setSelectedFeatureIds(new Set())
     setHighlightedPanelFeatureId(null)
+    setContextMenu(null)
   }, [])

   const importGeoJSON = useCallback((importedFeatures: GeoJSON.Feature[], mode: 'replace' | 'merge') => {
     if (mode === 'replace') {
@@
       setFeatures({ type: 'FeatureCollection', features: importedFeatures })
       setDraftCoords([])
       setSelectedFeatureIds(new Set())
       setHighlightedPanelFeatureId(null)
+      setContextMenu(null)
     } else {
       setFeatures((prev) => ({ ...prev, features: [...prev.features, ...importedFeatures] }))
     }
   }, [])
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/hooks/useDrawingEngine.ts` around lines 427 - 467, After
deleting, resetting or replacing features you must also close the context menu;
update deleteSelectedFeatures, resetGeoJSON, and the 'replace' branch inside
importGeoJSON to clear the context menu state (call the same state updater used
for contextMenu, e.g. setContextMenu(null) or equivalent) immediately after the
feature/state changes and any highlightTimer cleanup so the menu cannot remain
open for features that no longer exist.

ポリゴンやラインの特定の頂点を削除する機能を実装:
- 頂点を右クリック → コンテキストメニュー「この頂点を削除」
- 頂点をクリック選択(赤くハイライト) → Delete/Backspaceキーで削除
- 最小頂点数チェック(LineString: 2, Polygon: 3)で削除不可の場合はdisabled表示
- ポリゴンのリング閉じ処理を維持
- Undo/Redo対応(既存のonCommitパターンを利用)

新規ファイル:
- vertex-helpers.ts: canDeleteVertex / applyVertexDelete ユーティリティ
- VertexContextMenu コンポーネント(glassmorphismスタイル)
- E2Eテスト: 5シナリオ(右クリック削除、最小頂点数無効、ポリゴン削除、Deleteキー、Undo復元)
- ユニットテスト: 12テストケース
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/drawing-engine/hooks/useVertexEditing.ts (1)

72-80: ⚠️ Potential issue | 🟠 Major

Polygon頂点移動で内周リング(holes)が消えます。

Line 79 が coordinates: [ring] 固定のため、外周以外のリングが失われます。移動操作でデータ欠損が起きるので保持が必要です。

修正案
   if (geom.type === 'Polygon') {
-    const ring = geom.coordinates[0].map((c, i) => {
+    const rings = geom.coordinates
+    const ring = rings[0].map((c, i) => {
       if (i === vertexIndex) return newCoord
       // 頂点 0 を動かす場合、閉じる点も更新
-      if (vertexIndex === 0 && i === geom.coordinates[0].length - 1) return newCoord
+      if (vertexIndex === 0 && i === rings[0].length - 1) return newCoord
       return c
     }) as [number, number][]
-    return { ...feature, geometry: { ...geom, coordinates: [ring] } }
+    return { ...feature, geometry: { ...geom, coordinates: [ring, ...rings.slice(1)] } }
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/hooks/useVertexEditing.ts` around lines 72 - 80, The code
that handles Polygon vertex moves replaces geometry.coordinates with only
[ring], which drops any interior rings/holes; update the return to preserve all
existing rings by replacing only the outer ring: compute ring as you already do
and then set geometry.coordinates to [ring, ...geom.coordinates.slice(1)] (or
otherwise copy/merge the remaining rings) so holes are retained; keep the
existing logic for updating the closing point when vertexIndex === 0 and still
return { ...feature, geometry: { ...geom, coordinates: [ring,
...geom.coordinates.slice(1)] } }.
🧹 Nitpick comments (1)
src/drawing-engine/lib/__tests__/vertex-helpers.test.ts (1)

92-128: Polygonの内周リング(holes)保持ケースも追加してください。

現行のPolygonテストは外周リングのみで、内周リング消失の回帰を検知できません。削除後に coordinates.slice(1) が維持される検証を1件追加するのが安全です。

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/lib/__tests__/vertex-helpers.test.ts` around lines 92 -
128, Add a test that verifies polygon inner rings (holes) are preserved after
vertex deletion: call applyVertexDelete on a Feature with both an outer ring and
at least one inner ring, perform a deletion (e.g., delete an outer or inner
vertex) and assert that the coordinates array still contains the original inner
rings (e.g., coordinates.slice(1) equals the prior inner ring(s)) and that each
ring remains properly closed; reference applyVertexDelete and the
geometry.coordinates structure when locating where to add the assertion.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/drawing-engine/components/VertexContextMenu.css`:
- Around line 7-12: Remove the empty line that precedes the background
declaration so the `background:` property is immediately followed by its
`linear-gradient(...)` value (and ensure `backdrop-filter:` remains directly
after the background block); this fixes the `declaration-empty-line-before`
Stylelint rule violation for the `background` declaration in
VertexContextMenu.css.

In `@src/drawing-engine/lib/vertex-helpers.ts`:
- Around line 28-33: The current Polygon edit logic replaces
geometry.coordinates with only the edited outer ring (closed), dropping any
inner rings (holes); update the return to preserve existing holes by setting
geometry.coordinates to an array combining the edited outer ring and the
remaining rings (e.g. [closed, ...geom.coordinates.slice(1)]), ensuring you
reference the same feature and geom objects and still handle cases with no holes
safely when computing ring/newRing/closed based on vertexIndex.

---

Outside diff comments:
In `@src/drawing-engine/hooks/useVertexEditing.ts`:
- Around line 72-80: The code that handles Polygon vertex moves replaces
geometry.coordinates with only [ring], which drops any interior rings/holes;
update the return to preserve all existing rings by replacing only the outer
ring: compute ring as you already do and then set geometry.coordinates to [ring,
...geom.coordinates.slice(1)] (or otherwise copy/merge the remaining rings) so
holes are retained; keep the existing logic for updating the closing point when
vertexIndex === 0 and still return { ...feature, geometry: { ...geom,
coordinates: [ring, ...geom.coordinates.slice(1)] } }.

---

Nitpick comments:
In `@src/drawing-engine/lib/__tests__/vertex-helpers.test.ts`:
- Around line 92-128: Add a test that verifies polygon inner rings (holes) are
preserved after vertex deletion: call applyVertexDelete on a Feature with both
an outer ring and at least one inner ring, perform a deletion (e.g., delete an
outer or inner vertex) and assert that the coordinates array still contains the
original inner rings (e.g., coordinates.slice(1) equals the prior inner ring(s))
and that each ring remains properly closed; reference applyVertexDelete and the
geometry.coordinates structure when locating where to add the assertion.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2246d15 and 8c7ff2c.

📒 Files selected for processing (11)
  • e2e/features/vertex-deletion.feature
  • e2e/step-definitions/vertex-deletion-steps.ts
  • e2e/support/helpers/selectors.ts
  • src/components/MapView.tsx
  • src/drawing-engine/components/VertexContextMenu.css
  • src/drawing-engine/components/VertexContextMenu.tsx
  • src/drawing-engine/hooks/useDrawingEngine.ts
  • src/drawing-engine/hooks/useVertexEditing.ts
  • src/drawing-engine/index.ts
  • src/drawing-engine/lib/__tests__/vertex-helpers.test.ts
  • src/drawing-engine/lib/vertex-helpers.ts
✅ Files skipped from review due to trivial changes (1)
  • e2e/features/vertex-deletion.feature

Comment on lines +7 to +12

background:
linear-gradient(160deg,
rgba(255, 255, 255, 0.92) 0%,
rgba(237, 233, 254, 0.8) 100%);
backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Stylelint違反(空行)を解消してください。

Line 8 の宣言前空行で declaration-empty-line-before に違反しています。

修正案
 .vertex-context-menu {
   position: fixed;
   z-index: 100;
   min-width: 160px;
   padding: 6px;
   border-radius: 10px;
-
   background:
     linear-gradient(160deg,
       rgba(255, 255, 255, 0.92) 0%,
       rgba(237, 233, 254, 0.8) 100%);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
background:
linear-gradient(160deg,
rgba(255, 255, 255, 0.92) 0%,
rgba(237, 233, 254, 0.8) 100%);
backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
.vertex-context-menu {
position: fixed;
z-index: 100;
min-width: 160px;
padding: 6px;
border-radius: 10px;
background:
linear-gradient(160deg,
rgba(255, 255, 255, 0.92) 0%,
rgba(237, 233, 254, 0.8) 100%);
backdrop-filter: blur(var(--de-glass-blur-strong, 24px));
}
🧰 Tools
🪛 Stylelint (17.3.0)

[error] 8-11: Unexpected empty line before declaration (declaration-empty-line-before)

(declaration-empty-line-before)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/components/VertexContextMenu.css` around lines 7 - 12,
Remove the empty line that precedes the background declaration so the
`background:` property is immediately followed by its `linear-gradient(...)`
value (and ensure `backdrop-filter:` remains directly after the background
block); this fixes the `declaration-empty-line-before` Stylelint rule violation
for the `background` declaration in VertexContextMenu.css.

Comment on lines +28 to +33
if (geom.type === 'Polygon') {
const ring = geom.coordinates[0].slice(0, -1) // 閉じ点を除去
const newRing = ring.filter((_, i) => i !== vertexIndex)
// ポリゴンリングを閉じる(先頭 = 末尾)
const closed = [...newRing, newRing[0]]
return { ...feature, geometry: { ...geom, coordinates: [closed] } }
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Polygon編集時に内周リング(holes)が消失します。

Line 33 で coordinates: [closed] を返しており、既存の内周リングが破棄されます。Polygonデータを壊すため修正が必要です。

修正案
   if (geom.type === 'Polygon') {
-    const ring = geom.coordinates[0].slice(0, -1) // 閉じ点を除去
+    const rings = geom.coordinates
+    const ring = rings[0].slice(0, -1) // 閉じ点を除去
     const newRing = ring.filter((_, i) => i !== vertexIndex)
     // ポリゴンリングを閉じる(先頭 = 末尾)
     const closed = [...newRing, newRing[0]]
-    return { ...feature, geometry: { ...geom, coordinates: [closed] } }
+    return { ...feature, geometry: { ...geom, coordinates: [closed, ...rings.slice(1)] } }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (geom.type === 'Polygon') {
const ring = geom.coordinates[0].slice(0, -1) // 閉じ点を除去
const newRing = ring.filter((_, i) => i !== vertexIndex)
// ポリゴンリングを閉じる(先頭 = 末尾)
const closed = [...newRing, newRing[0]]
return { ...feature, geometry: { ...geom, coordinates: [closed] } }
if (geom.type === 'Polygon') {
const rings = geom.coordinates
const ring = rings[0].slice(0, -1) // 閉じ点を除去
const newRing = ring.filter((_, i) => i !== vertexIndex)
// ポリゴンリングを閉じる(先頭 = 末尾)
const closed = [...newRing, newRing[0]]
return { ...feature, geometry: { ...geom, coordinates: [closed, ...rings.slice(1)] } }
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/drawing-engine/lib/vertex-helpers.ts` around lines 28 - 33, The current
Polygon edit logic replaces geometry.coordinates with only the edited outer ring
(closed), dropping any inner rings (holes); update the return to preserve
existing holes by setting geometry.coordinates to an array combining the edited
outer ring and the remaining rings (e.g. [closed,
...geom.coordinates.slice(1)]), ensuring you reference the same feature and geom
objects and still handle cases with no holes safely when computing
ring/newRing/closed based on vertexIndex.

@sugama-satsuki
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sugama-satsuki sugama-satsuki changed the title 描画エンジンをsrc/drawing-engine/に分離 描画エンジンをsrc/drawing-engine/に分離 + 頂点削除機能を追加 Mar 2, 2026
@sugama-satsuki
Copy link
Collaborator Author

E2E テスト結果

57 scenarios (57 passed)
366 steps (366 passed)
2m04.328s (executing steps: 2m03.516s)

頂点削除テスト (5シナリオ追加)

  • ラインの頂点を右クリックで削除できる ✅
  • ラインの最小頂点数(2)では削除メニューが無効 ✅
  • ポリゴンの頂点を右クリックで削除できる ✅
  • 頂点をクリック選択してDeleteキーで削除できる ✅
  • 頂点削除後にUndoで復元される ✅

ユニットテスト結果

9 test files, 98 tests passed

@sugama-satsuki
Copy link
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Mar 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@sugama-satsuki sugama-satsuki merged commit fa8d2f5 into main Mar 2, 2026
2 checks passed
@sugama-satsuki sugama-satsuki deleted the refactor/drawing-engine branch March 2, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant