Skip to content

feat(web): a step inside a repeat cannot be dragged, duplicated, or added as anything but steady #1004

Description

@janlauber

Building 4 × (3 min at threshold / 1 min ramp / 2 min easy) means fighting the editor, because a step inside a repeat can do less than the same step outside one.

Mockups: https://claude.ai/code/artifact/6c6f89dc-e3e6-4370-a690-7d792c7ee667 — middle column.

top-level step inside a repeat
drag to reorder yes nodraggable={top} (edit/+page.svelte:312)
what you can add steady · ramp · sprint · repeat steady only (addInto(), :151)
click it on the graph selects it selects its parentonSelect={(i) => (selected = [i])} (:263)
duplicate it no no
a context menu no no

The state layer is not the problem: stepAt() already walks an arbitrary path, move() and remove() already take a path and work at any depth, and siblingsOf() already resolves a parent's children. Three UI affordances stopped at one level while the model underneath never did.

addInto() even says so out loud:

// ponytail: steady only — over-unders are steady pairs; other types via top-level

That was a fair shortcut when repeats were new. It is now the reason a rider cannot put a ramp inside an interval set.

What changes

Drag works at every depth. The current handler indexes workout.steps directly (dropStep(), :164) — it becomes path-based, dropping within the same parent. Cross-parent drags (out of a repeat, or into one) are the harder case: allow it if it falls out cheaply, and if not, say so in a ponytail: comment naming the limit rather than leaving it silently missing.

Every step type can be added anywhere. addInto() takes the type, and the nested add row renders the same four buttons the top level does.

The graph selects the step you clicked, at whatever depth. IntervalGraph's onSelect carries a path rather than a top-level index — flatten() already knows which step produced each segment; the block needs to carry that path out.

Duplicate exists, for a step and for a whole repeat block. This is the single biggest lever on "clunky": an interval set is built by making one block right and then copying it.

A step gets a context menuux.md requires it for any object with more than one action, and a step has five. Right-click on a desk, long-press on touch, using contextMenu from $lib/context-menu.svelte:

┌────────────────────────────────┐
│ ⧉  Duplicate            ⌘D    │
│ ↑  Move up              ⌘↑    │
│ ↓  Move down            ⌘↓    │
├────────────────────────────────┤
│ ⟲  Wrap in a repeat            │
├────────────────────────────────┤
│ 🗑  Delete                ⌫    │   ← danger token, last
└────────────────────────────────┘

Nothing lives only in the menu: the arrows stay inline, delete stays in the inspector, and duplicate gets a visible affordance too.

Structure

edit/+page.svelte is 583 lines against code-quality.md's 500-line ceiling, and this adds to it. Split the surface this issue touches, in this PR:

edit/+page.svelte   → page shell, workout state, save
StepList.svelte     → the tree, drag at depth, the context menu

Leave the inspector and the library sidebar for the issues that touch them.

Acceptance criteria

  • Drag reorders a step inside a repeat, not only at the top level.
  • All four step types can be added inside a repeat.
  • Clicking any block on the graph selects that exact step, including a repeat's child.
  • Duplicate works on a step and on a whole repeat, and the copy lands directly after the original, selected.
  • Right-click and long-press open the step menu; every item works at every depth; delete takes the danger token and sits last.
  • Nothing in the menu is menu-only.
  • Any drag case deliberately not supported carries a ponytail: comment naming the limit.
  • StepList.svelte is split out and edit/+page.svelte is back under 500 lines.
  • Vitest: duplicate at depth, reorder at depth, add each type into a repeat, and the graph path round-trip.
  • make ci green.

Verify it for real

The verify skill: build 4 × (3 min threshold / 1 min ramp / 2 min easy) from an empty sheet without touching the top-level add buttons for the inner steps, duplicate the repeat, and confirm the graph and the zone bar follow.

Deliberately not in scope

Undo (#1005), direct manipulation on the graph (#1006), the hardcoded FTP (#1003), and the library sidebar's one-click replacement of your sheet.

Related

#1003, #1005, #1006, #255 (the previous intuitiveness pass — this is what it did not reach), #170, ux.md (context menus), code-quality.md (the size ceiling).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

designDesign related stuffenhancementNew feature or requestworkoutsworkout engine & player

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions