Repository navigation
feat: redesign phase 4 — Segment Anything first slices, selection bar, rest-of-image slice - #29
Merged
Merged
Conversation
…, rest-of-image slice HANDOFF.md §12 step 4: - SegmentPanel replaces the Segmentation tab: "Segment objects" with three sub-steps and a selection summary, New slice / Add to selected / Remove from selected; a collapsible "Split by depth" (slice count, bands, Split image, Balance), open until the first slices exist; and "Selected slice" with a depth slider and field, the ground toggle and Fit ground. The slice count moves here from Configuration. - SelectionBar floats under the image while a selection exists: Segment N points, Add to / Remove from the selected slice, New slice. The stage keeps its strip clear so the bar never covers the image. - Backend: the first object slice cut from a project with no slices also creates a farthest "rest of image" slice (input minus the object). It is kept in sync incrementally - later cuts and added masks subtract from it; deleting an object or removing mask pixels restores only pixels no other slice covers - so inpainting the rest slice is never undone. Persisted as `rest_slice`; SliceView.isRest; a "rest of image" badge. - e2e: the driver opens Split by depth before using it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Rest-slice syncing on mask removal restores alpha as fully opaque even for partial-alpha removals (e.g., feathered masks), which can violate the intended rest/object alpha relationship and produce incorrect composites.
Review effort: Lite
Findings: 1
Open (3)
What changed in this PR
Implements phase 4 of the UI redesign by making “Segment Anything first” slice creation the primary workflow, introducing a floating SelectionBar, and adding backend support for a persisted “rest of image” slice that stays incrementally synced with slice edits.
Changes:
- Frontend: Replace
SegmentationTabwithSegmentPaneland addSelectionBarfor SAM selection actions. - Backend/API: Add
ImageSlice.is_rest, persist it in project JSON (rest_slice), expose it asSliceView.isRest, and log first-cut creation. - Tests/E2E: Add backend + frontend tests for rest-slice behavior and update the Playwright driver for the collapsible “Split by depth” section.
| File | Description |
|---|---|
| parallax_maker/slice_editing_services.py | Creates/syncs the “rest of image” slice during create/delete/add-mask/remove-mask operations. |
| parallax_maker/slice.py | Adds is_rest flag to ImageSlice. |
| parallax_maker/controller.py | Persists/restores the rest-slice index in project JSON (rest_slice). |
| parallax_maker/api/schemas.py | Extends SliceView with is_rest (serialized as isRest). |
| parallax_maker/api/projects.py | Projects ImageSlice.is_rest into API slice views. |
| parallax_maker/api/slice_editing.py | Logs rest-slice creation using rest_slice_filename. |
| parallax_maker/e2e_server.py | Exposes rest_slice index via the e2e oracle state. |
| parallax_maker/test_slice_editing_services.py | Adds unit tests for rest-slice creation and sync semantics. |
| parallax_maker/test_api_slice_editing.py | Adds API test verifying isRest and the log line on first cut. |
| parallax_maker/test_controller.py | Adds JSON round-trip persistence tests for the rest-slice flag. |
| frontend/src/lib/components/shell/Inspector.svelte | Swaps SegmentationTab for the new SegmentPanel. |
| frontend/src/lib/components/segmentation/SegmentPanel.svelte | New “Slices panel” UI: SAM-first steps, collapsible “Split by depth”, selected slice controls. |
| frontend/src/lib/components/segmentation/SegmentPanel.test.ts | New unit tests for SegmentPanel behavior and API calls. |
| frontend/src/lib/components/segmentation/SegmentationTab.svelte | Removed legacy Segmentation inspector tab UI. |
| frontend/src/lib/components/segmentation/SegmentationTab.test.ts | Removed tests for the legacy Segmentation tab. |
| frontend/src/lib/components/canvas/SelectionBar.svelte | New floating toolbar for queued points / mask actions while a selection exists. |
| frontend/src/lib/components/canvas/SelectionBar.test.ts | New unit tests for SelectionBar rendering and actions. |
| frontend/src/lib/components/canvas/CanvasArea.svelte | Renders SelectionBar and reserves space so it doesn’t cover the image. |
| frontend/src/lib/components/layers/LayerPanel.svelte | Displays a “rest of image” badge for the rest slice. |
| frontend/src/lib/components/layers/LayerPanel.test.ts | Tests that only the rest slice shows the new badge. |
| frontend/src/lib/components/config/ConfigurationTab.svelte | Removes slice-count slider (moved under “Split by depth” in SegmentPanel). |
| frontend/src/lib/api/schema.json | Adds isRest to the frontend API schema. |
| frontend/src/lib/api/generated.ts | Adds generated isRest typing to SliceView. |
| e2e/drivers/svelte.ts | Ensures “Split by depth” is expanded before clicking split/balance controls. |
| docs/svelte-migration/ARCHITECTURE.md | Documents new rest-slice behavior in the HTTP contract. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+492
to
+504
| if rest_slice is not None: | ||
| source = self._require_image(state) | ||
| # The area the mask actually removed coverage from; restored into | ||
| # the rest slice only where nothing else still covers it. | ||
| exposed = (mask > 0) & (before_alpha > 0) | ||
| other_slices = [ | ||
| s | ||
| for s in state.image_slices | ||
| if s is not object_slice and s is not rest_slice | ||
| ] | ||
| self._restore_uncovered_into_rest( | ||
| rest_slice, np.array(source.convert("RGB")), exposed, other_slices | ||
| ) |
| | `POST /api/v1/projects/{id}/slices/{index}/add-mask` | `{}` | `200 ProjectView & {changed: true}` (sync); `add_mask_to_slice`. `index` must equal the project's current `selectedSlice` (`400 invalid_request` otherwise, since the service itself has no index parameter - it always operates on the selection, exactly like Dash). | | ||
| | `POST /api/v1/projects/{id}/slices/{index}/remove-mask` | `{}` | Same shape and `index`-must-match-selection rule as add-mask, calling `remove_mask_from_slice`. | | ||
| | `POST /api/v1/projects/{id}/slices/create` | `{}` | `200 ProjectView & {changed: true}` (sync); `create_slice` - a new slice from the current mask (or an empty transparent one with none), appended, selected, and saved (JSON + the new slice's own image). When Segment Anything cuts the *first* object slice out of a project with no slices yet, a farthest "rest of image" slice is also created (`SliceView.isRest`): the input image with alpha `255 - object.alpha`, depth 0, logged as `"Created image_slice_N with the rest of the image"`. The selection stays on the object slice even though the rest slice's depth-0 insertion can shift its index. If a rest slice already exists, its alpha is instead reduced by the new object's footprint (`rest.alpha = min(rest.alpha, 255 - object.alpha)`), saved as a new (undoable) version. A project split by depth first (slices exist, none is the rest) behaves exactly as before - no rest slice is added. An empty create (no mask) never creates or changes the rest slice. Persisted as `rest_slice` in the project JSON, the same way `ground_plane_slice` is. | | ||
| | `DELETE /api/v1/projects/{id}/slices/{index}` | – | `200 ProjectView & {changed: true}` (sync); `delete_slice` - out-of-range `index` is `400 invalid_request`; the selection is always cleared. When a rest slice exists and an *object* slice is deleted, pixels the deleted slice covered that no remaining (non-rest) slice covers either are restored into the rest slice from the input image (RGB + alpha 255); every other rest pixel - including any inpainting done directly on it - is left alone, and the rest slice is saved as a new version. Deleting the rest slice itself just clears the flag; none is recreated automatically. | |
Comment on lines
+716
to
+718
| def test_delete_slice_of_the_rest_slice_itself_just_clears_the_flag( | ||
| tmp_path: Path, | ||
| ) -> None: |
…slice The rest slice's alpha now rises to 255 - the object's remaining alpha, only where the edit lowered it, and RGB is refilled only where the rest was fully transparent, so partial removals keep rest.alpha <= 255 - object.alpha and visible rest pixels stay as they were. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Phase 4 of the UI redesign (docs/redesign/HANDOFF.md, §12 step 4): making slices is now Segment Anything first, and cutting objects no longer leaves holes.
Slices panel (
SegmentPanel, replaces the Segmentation tab)SelectionBarBackend: a "rest of image" slice, kept in sync. This was the product decision when the handoff was reviewed.
rest_slicein the project JSON, exposed asSliceView.isRest, and shown with a "rest of image" badge in the layer panel.Tests
isRestplus the log line.SegmentPanelandSelectionBartests, and the rest badge.image_slice_0 · rest of image, and the Composite view has no holes.Next: phase 5, the Inpaint, Ground and Preview panels.
🤖 Generated with Claude Code