Canvas view with activity-based card sizing #40

Open
opened 2026-06-10 01:38:14 +00:00 by ldraney · 3 comments
Owner

Type

Feature

Lineage

Depends on ldraney/palinks #36 (clicks table) for activity data.
Prerequisite for ldraney/palinks #41 (link groups).

Repo

ldraney/palinks

User Story

As a palinks user
I want my links displayed on a spatial canvas where frequently-used links appear larger
So that my most valuable links are immediately visible and I can organize links spatially

Context

palinks currently renders links in a flat CSS grid with manual drag-and-drop ordering. The vision is to replace this with a freeform canvas where cards are absolutely positioned and their size scales dynamically based on click activity — more clicks = bigger card, rarely used = smaller card. This creates an organic, activity-driven layout where the most valuable links naturally surface.

Architecture decision: HTML/CSS canvas with absolute positioning and transform: scale() rather than <canvas>/WebGL. This preserves Turbo/Stimulus integration, native DOM link behavior, and is dramatically simpler to build for the scale of a personal link manager.

File Targets

Files the agent should modify or create:

  • db/migrate/*_add_canvas_position_to_links.rb — add canvas_x, canvas_y float columns to links (nullable, null = needs initial placement)
  • app/models/link.rb — canvas position defaults, scale calculation method based on click count
  • app/views/links/index.html.erb — replace grid layout with canvas container
  • app/views/links/_link.html.erb — add inline style for position/scale transforms
  • app/javascript/controllers/canvas_controller.js — pan (click-drag on background), zoom (scroll wheel), drag-to-reposition cards
  • app/assets/stylesheets/application.css — canvas container styles, card scaling transitions

Files the agent should NOT touch:

  • app/javascript/controllers/sortable_controller.js — legacy grid ordering, will be deprecated separately

Feature Flag

Flag: none
Rationale: The target flag is canvas_layout but the feature_flags table (#34) and feature_enabled? helper don't exist yet. Canvas view ships as the default layout; flag gate added when #34 lands.

Acceptance Criteria

  • Links render on a canvas with absolute positioning instead of a grid
  • Card size scales proportionally to click count (min scale 0.7, max scale 1.5)
  • Cards can be dragged to new positions on the canvas, position persists on reload
  • Canvas supports pan (click-drag on empty background) and zoom (scroll wheel)
  • Links with zero clicks render at base size (scale 1.0)
  • Links with no canvas_x/canvas_y (existing links, newly created) are auto-placed in a grid pattern on first render, then position persists after that
  • New links created after canvas is live get auto-placed at a sensible default position (e.g. center of viewport or next grid slot)

Test Expectations

  • Unit test: Link#canvas_scale returns correct scale factor based on click count relative to max
  • Unit test: canvas position columns are nullable (null = needs auto-placement)
  • Integration test: dragging a card persists new x/y coordinates via PATCH
  • Integration test: link with null canvas_x/canvas_y gets auto-placed on render
  • Run command: bin/rails test

Constraints

  • Must depend on #36 (clicks table) being merged first — scale calculation reads click counts
  • Use CSS transform: scale() for card sizing, not width/height manipulation
  • Respect existing Stimulus controller patterns in the codebase
  • Min scale 0.7, max scale 1.5 to keep cards readable
  • Auto-placement for existing links: arrange in a grid pattern based on current position column, then save canvas_x/canvas_y so it only happens once
  • Pan: click-drag on canvas background (not on cards). Zoom: scroll wheel. No pinch-to-zoom required (desktop-first).

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • palinks — project this affects
  • #36 — clicks table (direct dependency)
  • #41 — link groups (depends on this)
### Type Feature ### Lineage Depends on `ldraney/palinks #36` (clicks table) for activity data. Prerequisite for `ldraney/palinks #41` (link groups). ### Repo `ldraney/palinks` ### User Story As a palinks user I want my links displayed on a spatial canvas where frequently-used links appear larger So that my most valuable links are immediately visible and I can organize links spatially ### Context palinks currently renders links in a flat CSS grid with manual drag-and-drop ordering. The vision is to replace this with a freeform canvas where cards are absolutely positioned and their size scales dynamically based on click activity — more clicks = bigger card, rarely used = smaller card. This creates an organic, activity-driven layout where the most valuable links naturally surface. Architecture decision: HTML/CSS canvas with absolute positioning and `transform: scale()` rather than `<canvas>`/WebGL. This preserves Turbo/Stimulus integration, native DOM link behavior, and is dramatically simpler to build for the scale of a personal link manager. ### File Targets Files the agent should modify or create: - `db/migrate/*_add_canvas_position_to_links.rb` — add `canvas_x`, `canvas_y` float columns to links (nullable, null = needs initial placement) - `app/models/link.rb` — canvas position defaults, scale calculation method based on click count - `app/views/links/index.html.erb` — replace grid layout with canvas container - `app/views/links/_link.html.erb` — add inline style for position/scale transforms - `app/javascript/controllers/canvas_controller.js` — pan (click-drag on background), zoom (scroll wheel), drag-to-reposition cards - `app/assets/stylesheets/application.css` — canvas container styles, card scaling transitions Files the agent should NOT touch: - `app/javascript/controllers/sortable_controller.js` — legacy grid ordering, will be deprecated separately ### Feature Flag Flag: none Rationale: The target flag is `canvas_layout` but the feature_flags table (#34) and `feature_enabled?` helper don't exist yet. Canvas view ships as the default layout; flag gate added when #34 lands. ### Acceptance Criteria - [ ] Links render on a canvas with absolute positioning instead of a grid - [ ] Card size scales proportionally to click count (min scale 0.7, max scale 1.5) - [ ] Cards can be dragged to new positions on the canvas, position persists on reload - [ ] Canvas supports pan (click-drag on empty background) and zoom (scroll wheel) - [ ] Links with zero clicks render at base size (scale 1.0) - [ ] Links with no canvas_x/canvas_y (existing links, newly created) are auto-placed in a grid pattern on first render, then position persists after that - [ ] New links created after canvas is live get auto-placed at a sensible default position (e.g. center of viewport or next grid slot) ### Test Expectations - [ ] Unit test: Link#canvas_scale returns correct scale factor based on click count relative to max - [ ] Unit test: canvas position columns are nullable (null = needs auto-placement) - [ ] Integration test: dragging a card persists new x/y coordinates via PATCH - [ ] Integration test: link with null canvas_x/canvas_y gets auto-placed on render - Run command: `bin/rails test` ### Constraints - Must depend on #36 (clicks table) being merged first — scale calculation reads click counts - Use CSS `transform: scale()` for card sizing, not width/height manipulation - Respect existing Stimulus controller patterns in the codebase - Min scale 0.7, max scale 1.5 to keep cards readable - Auto-placement for existing links: arrange in a grid pattern based on current `position` column, then save canvas_x/canvas_y so it only happens once - Pan: click-drag on canvas background (not on cards). Zoom: scroll wheel. No pinch-to-zoom required (desktop-first). ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `palinks` — project this affects - `#36` — clicks table (direct dependency) - `#41` — link groups (depends on this)
Author
Owner

Scope Review: NEEDS_REFINEMENT

Review note: review-1406-2026-06-09
Template is complete and file targets all verified, but traceability and dependency gaps need attention before this moves to next_up.

  • [SCOPE] story:canvas-layout is not listed in the project-palinks user-stories section -- add it
  • [BODY] Feature flag AC (#6) depends on #34 (feature_flags table), which is in backlog and not documented as a dependency. Either add #34 as an explicit dependency or simplify to an env-var gate
  • [BODY] Add AC for initial canvas placement of existing links that have no x/y position data
  • [BODY] Clarify pan/zoom interaction methods in AC #4 (scroll wheel? pinch? buttons?)
## Scope Review: NEEDS_REFINEMENT Review note: `review-1406-2026-06-09` Template is complete and file targets all verified, but traceability and dependency gaps need attention before this moves to next_up. - **[SCOPE]** `story:canvas-layout` is not listed in the `project-palinks` user-stories section -- add it - **[BODY]** Feature flag AC (#6) depends on #34 (feature_flags table), which is in backlog and not documented as a dependency. Either add #34 as an explicit dependency or simplify to an env-var gate - **[BODY]** Add AC for initial canvas placement of existing links that have no x/y position data - **[BODY]** Clarify pan/zoom interaction methods in AC #4 (scroll wheel? pinch? buttons?)
Author
Owner

Scope refinement (2026-06-09):

Changes from scope review review-1406-2026-06-09:

  1. Feature flag dependency on #34 removed — changed to "none" with rationale matching #36's approach: ship ungated, flag gate added when feature_flags table (#34) lands. This removes the undocumented dependency.
  2. Initial placement AC added — new AC: "Links with no canvas_x/canvas_y are auto-placed in a grid pattern on first render." Covers existing links and newly created links.
  3. Pan/zoom interactions specified — AC now says "pan (click-drag on empty background) and zoom (scroll wheel)". Added constraint: no pinch-to-zoom required (desktop-first).
  4. canvas_x/canvas_y columns made nullable — null means "needs auto-placement", documented in file targets and test expectations.

Acknowledged but not blocking:

  • story:canvas-layout not in project-palinks user-stories — cross-cutting gap, same as #41
**Scope refinement (2026-06-09):** Changes from scope review `review-1406-2026-06-09`: 1. **Feature flag dependency on #34 removed** — changed to "none" with rationale matching #36's approach: ship ungated, flag gate added when feature_flags table (#34) lands. This removes the undocumented dependency. 2. **Initial placement AC added** — new AC: "Links with no canvas_x/canvas_y are auto-placed in a grid pattern on first render." Covers existing links and newly created links. 3. **Pan/zoom interactions specified** — AC now says "pan (click-drag on empty background) and zoom (scroll wheel)". Added constraint: no pinch-to-zoom required (desktop-first). 4. **canvas_x/canvas_y columns made nullable** — null means "needs auto-placement", documented in file targets and test expectations. Acknowledged but not blocking: - `story:canvas-layout` not in project-palinks user-stories — cross-cutting gap, same as #41
Author
Owner

Scope Review: APPROVED

Review note: review-1406-2026-06-09-r2

Re-review after refinement. All 4 items from the previous NEEDS_REFINEMENT review (review-1406-2026-06-09) have been addressed:

  • Feature flag dependency on #34 removed — ships ungated with Flag: none
  • Auto-placement AC added for links with null canvas_x/canvas_y
  • Pan/zoom interaction methods specified (click-drag background, scroll wheel, no pinch-to-zoom)
  • story:canvas-layout user story gap acknowledged as cross-cutting, not blocking (precedent from #36, #41)

Scope is solid. Ready for next_up.

## Scope Review: APPROVED Review note: `review-1406-2026-06-09-r2` Re-review after refinement. All 4 items from the previous NEEDS_REFINEMENT review (`review-1406-2026-06-09`) have been addressed: - Feature flag dependency on #34 removed — ships ungated with `Flag: none` - Auto-placement AC added for links with null `canvas_x`/`canvas_y` - Pan/zoom interaction methods specified (click-drag background, scroll wheel, no pinch-to-zoom) - `story:canvas-layout` user story gap acknowledged as cross-cutting, not blocking (precedent from #36, #41) Scope is solid. Ready for `next_up`.
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ldraney/palinks#40
No description provided.