Skip to content

chore(skills): rebuild skills index - #1

Open
github-actions[bot] wants to merge 1 commit into
mainfrom
skills-index-update
Open

chore(skills): rebuild skills index#1
github-actions[bot] wants to merge 1 commit into
mainfrom
skills-index-update

Conversation

@github-actions

@github-actions github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Automated rebuild of registry/skills-index.json from registry/sources.json.

DrOlu pushed a commit that referenced this pull request Jun 9, 2026
* feat(canvas): interactive ghost placement for new nodes (#237)

Replace immediate auto-placement of new canvas nodes with a "ghost" picker:
creating a node now shows ranked, translucent previews at smart candidate
spots and lets the user choose where it lands (click, number keys 1..N, or
Enter for the best spot; Esc / click-empty cancels).

- findFreePositions(): ranked candidate algorithm seeded from the focused node
  and its nearest neighbours, with on-screen-first ranking and a guaranteed
  fallback so crowded layouts never yield an empty list.
- canvasStore pendingPlacement state + beginPlacement/commitPlacement/
  cancelPlacement/setPlacementHover actions.
- placePanel fork: ambiguous (position-less) creates defer to ghosts; explicit-
  position paths (drag-drop, restore, right-click "new here") place as before.
  Cancelled placements roll the orphan panel record back.
- GhostPlacementLayer component rendered in the canvas world div.
- Unit tests for the candidate algorithm and placement actions.

* feat(canvas): smarter ghost placement — no overlap, mouse-anchored, multi-size

Address review feedback on the ghost picker:

- No overlapping recommendations: candidates are now rejected if their rect
  overlaps any existing node OR an already-accepted candidate (the prior version
  only checked existing nodes, so adjacent ghosts could overlap each other).
- Anchor to the mouse: recommendations are generated as a ring around the last
  canvas-space pointer position and ranked by proximity to it (falls back to the
  focused node, then viewport centre). Canvas tracks the pointer rAF-throttled
  via a non-reactive store setter.
- Zoom out to show recommendations: beginPlacement frames all ghosts (plus the
  focused node) by zooming OUT only; commit restores the prior zoom and centres
  the new node, cancel restores the full viewport.
- Multiple aspect ratios: placementSizeVariants offers Standard/Wide/Tall/Large
  per panel type; candidates cycle variants for shape variety and each ghost
  shows its size label. The committed node uses the chosen candidate's size.

findFreePositions is replaced by findPlacementCandidates (point+size+label).
Tests updated and extended (no-overlap, mouse anchor, variants, zoom-out/restore).

* feat(canvas): cursor-anchored, live-following ghost placement

Replace the fixed rosette of aspect-ratio ghosts with a cursor-driven cluster:

- placementCluster() builds a small cluster anchored at the cursor — a primary
  "drop here" ghost centred on the mouse, with Wide/Tall/Large alternates tucked
  beside it, each nudged to the nearest free spot so nothing overlaps a node or
  another ghost.
- The cluster follows the mouse live: a full-bleed placement surface (above
  nodes, below ghosts) tracks the cursor (rAF-throttled) and re-anchors the
  cluster via updatePlacementCursor; clicking anywhere drops the primary, so the
  whole canvas becomes "click to place here". Numbered ghosts / 1..N / Enter pick
  a specific shape; Esc cancels.
- Drop the auto zoom-out — it fought cursor anchoring (moved recommendations
  away from where you point). Placement now happens at the current zoom, right
  under the cursor.

findPlacementCandidates (rays+ring rosette) is removed in favour of
placementCluster. Tests updated for the cursor-anchored model + updatePlacementCursor.

* feat(canvas): recommendation picker — zoom out + 3-5 numbered ghosts

Settle on a single creation model: trigger a create → the canvas zooms out and
shows 3–5 recommended spots as numbered ghosts; the user picks by clicking one
or pressing its number (Enter = best, Esc / click-empty cancels).

- recommendPlacements(): up to 5 ranked, non-overlapping recommended positions
  (rays past obstacles from the focused node + its neighbours, plus a ring near
  the anchor), all at the panel's default size. Ranked by proximity to the
  anchor (mouse → focused node → viewport centre), on-screen first; never
  overlaps a node or another recommendation.
- Zoom out to frame all recommendations (+ focused node); restore the prior
  zoom on commit (then centre the new node) and the full viewport on cancel.
- Remove the cursor-following cluster + placement surface + updatePlacementCursor
  and the per-ghost aspect-ratio labels; ghosts are now clean numbered targets.

* feat(canvas): polish picker, add click-anywhere escape hatch + toolbar drag

Three improvements to new-terminal creation:

1. Escape hatch — "what if no recommendation fits?": the dimmed placement
   surface is now clickable. Hovering empty canvas previews a free spot ("Place
   here"), and clicking anywhere drops the terminal there (nudged to the nearest
   non-overlapping grid spot). New store bits: pendingPlacement.freeGhost,
   updatePlacementCursor, commitFreePlacement, and the nudgeToFree helper.

2. Picker polish: a dim scrim focuses attention, numbered ghosts animate in with
   a stagger + hover glow and a "Best" tag on #1, and a fixed hint pill (portal)
   explains the controls (1–N / click / Esc) with a Cancel button.

3. Toolbar drag-to-place: dragging the toolbar terminal icon onto the canvas
   spawns a ghost that follows the cursor; releasing drops a terminal at that
   exact spot (explicit position → bypasses the picker). A plain click still
   opens the recommendation picker.

Tests added for nudgeToFree, updatePlacementCursor, and commitFreePlacement.

* feat(canvas): placement setting (auto vs picker), tidier recommendations, hint fix

- Setting (Canvas → "Recommend where new panels go", default ON): when enabled,
  Cmd+T / toolbar click open the recommendation picker; when off, the panel is
  auto-placed in the best spot immediately (no zoom-out, no ghosts). Gated in
  placePanel via settingsStore. New AppSettings.placementPicker + store schema.
- Recommendations now align to the existing layout: candidates are the four
  edge-adjacent slots of each node (aligned to its top/left edge, uniform gap),
  kept only if they clear every node AND other ghosts by a full gap — no more
  touching ghosts or oversized gaps. Replaces the ray/ring generator.
- Hint pill moved to top-centre so it no longer overlaps the bottom toolbar.

* feat(canvas): context-aware placement recommendations

Recommendations now adapt to what the user is focused on instead of scattering
spots around every node:

- Active node (the focused node when on screen, or a single node dominating the
  viewport when zoomed in) → recommend tightly AROUND that node: its four
  directions, hopping just past an immediate neighbour when an edge is blocked.
  The active node is the centre of the recommendations.
- No active node, viewport blank (panned to empty space) → recommend centred on
  where the user is looking.
- No active node, nodes on screen → group nodes into islands (union-find by
  edge-gap), pick the island nearest the anchor (cursor/viewport centre), and
  recommend around its perimeter — more, further-flung spots.
- Empty canvas → a few spots centred on the anchor.

Adds geometry helpers (rectGap, intersectionArea, pointRectDistance,
clusterNodes, boundsOf). Tests cover each mode.

* feat(canvas): contiguous active-node recs, F-gated free placement, focused picker chrome

- Active-node recommendations now form ONE connected group next to the node via
  an edge-anchored lattice + BFS that only accepts free cells and never crosses
  an occupied one — so in a crowded cluster the ghosts stay attached to the
  node's free corner instead of scattering to opposite cluster edges.
- Free "place anywhere" is gated behind F: the "Place here" ghost no longer
  follows the cursor by default. Press F to arm it (crosshair + preview, click to
  drop); otherwise clicking empty canvas cancels. New pending.freeArmed +
  setFreeArmed.
- Picker chrome moved to screen space and focused: the hint pill is centred over
  the canvas (not the window) with the em dash removed and an F hint added; an
  app-wide dim with a bright cutout over the canvas + its tab bar recedes the
  sidebar/chrome while the canvas itself stays bright (transparent surface).

Tests: active-node cluster connectivity + F-arming lifecycle.

* feat(canvas): gap-fill recommendations with custom sizes

Sub-standard gaps between nodes now get their own recommendation, sized to fit
instead of being skipped:

- gapFillCandidates finds horizontal/vertical gaps between a cluster's nodes that
  are too narrow/short for a standard panel but at least PLACEMENT_MIN_W/H and
  within PLACEMENT_MIN_AR..MAX_AR aspect ratio, and emits a custom-sized rect
  centred in each (keeping the uniform gap to its neighbours).
- recommendPlacements now works on per-candidate rects (point + size), so
  standard and custom-sized ghosts mix freely; custom carry a small ranking
  penalty so a comparable standard spot still leads. Runs within the active
  node's cluster or the nearest island.

Tests: a sub-standard gap yields a custom ghost in the gap; a gap below the
minimum yields none.

* fix(canvas): dim sidebars + centre hint on the VISIBLE canvas

The canvas spans full-width behind the absolute-overlay sidebars, so the picker
overlay's canvas rect was the whole window — the hint landed at window-centre and
the dim box-shadow had nothing outside the window to darken.

Mark the sidebars (data-app-sidebar="left|right") and compute the visible canvas
strip between them: the dim cutout now actually darkens the sidebars (canvas + its
tab bar stay bright), and the hint pill is centred on the visible canvas, matching
the bottom toolbar.

* feat(canvas): adaptive recommendation size + fill gaps of any size

Recommendations now match the panels they sit beside instead of a fixed default:

- Adaptive size: in active mode the new panel matches the active node's size; in
  island mode it matches the cluster's median panel size — clamped to the
  placement min/max (280×180 … 1400×900) and aspect-ratio bounds. So in a layout
  of large terminals the ghosts are large too.
- Gap-fill now sizes a panel to FILL each between-node gap (clampPlacementSize),
  covering both narrower- AND wider-than-reference gaps, and emits it only when
  it differs from a reference-sized spot. Custom penalty lowered so these surface.
- recommendPlacements restructured: context (active/island/blank/empty) and the
  relevant cluster are resolved up front, then the adaptive size is derived.

Tests: active node size is matched; gap-fill positive/negative unchanged.

* feat(canvas): dim+block all app chrome during placement (except canvas tab)

The picker is now a true modal: everything outside the canvas is dimmed AND
non-interactive until a spot is placed or aborted.

- The dim is built from interactive rectangles (not a box-shadow) covering the
  whole window except the canvas content and its tab — so the header bar dims too
  while the active "Canvas" tab stays bright. Clicking any dimmed area cancels.
- In-canvas chrome (toolbar, minimap; tagged data-canvas-chrome) is made inert
  via a body.cate-placement-active class while a placement is pending. Existing
  nodes are already covered by the in-canvas surface.
- A transparent blocker keeps the bright canvas tab inert too.

* feat(canvas): fill irregular gaps via maximal empty rectangles

Gap-fill now detects the actual holes in a layout — including the irregular ones
a staggered arrangement leaves — instead of only gaps between two aligned nodes:

- findEmptyRects: coordinate-compress node edges within the cluster bounds into a
  free-cell grid and extract the maximal empty rectangles.
- gapFillCandidates sizes a panel to fill each hole (clampPlacementSize), emitted
  only when it differs from a reference-sized spot.
- A gap-filler now WINS its hole over the overlapping reference spot (small
  ranking preference), capped at ~half the slots so standards still show — so the
  individually-sized ghosts actually appear instead of being overlap-rejected.

Test: a staggered two-node layout yields a custom ghost filling an irregular hole.

* refactor(canvas): drop the placement dim/block overlay

The picker no longer dims or freezes the rest of the app. The free "place
anywhere" surface only mounts while free mode is armed (F); otherwise the app
stays fully interactive and you pick a ghost directly (click / number / Enter),
Esc or an empty-canvas click cancels. Removes the full-window dim rectangles,
the canvas-tab cutout, the chrome-disable body class + CSS, and the
data-canvas-chrome markers. PlacementOverlay shrinks to a small PlacementHint.

* refactor(canvas): centralize placement algorithm in placement.ts

Move the whole recommendation algorithm — recommendPlacements, findFreePosition,
nudgeToFree, the geometry helpers (clustering, empty-rectangle finding, gap-fill,
clamping) and the placement constants/types — out of the 2k-line canvas store
into one focused, pure, dependency-free module (canvas/placement.ts). The store
now just imports it and owns the picker state/actions; rendering lives in
GhostPlacementLayer. Drops the dead placementSizeVariants/PlacementSizeVariant.

No behaviour change; tests updated to import from the new module.

* fix(canvas): recommend the standard panel size around the active node

Recommendations now always use the standard default panel size instead of
mimicking the active node's (possibly unusual) shape — a new terminal next to a
tall/narrow window should still be a normal terminal. Only gap-fill candidates
take a custom size, to fit an irregular hole. Drops the now-unused adaptive
sizing + medianNodeSize helper.

* fix(canvas): fill big gaps beyond the cluster threshold with custom ghosts

Recommendations prefer the standard panel size, but individualize for holes a
standard panel doesn't fit — including BIG gaps. Gap-fill previously ran only
within the focus node's tight cluster, so two nodes farther apart than the
clustering threshold left their (large) gap unfilled. It now scans the focus
plus every node within ~2 panels of it, so a wide gap between far-apart nodes
gets a larger custom ghost that fills it.

Test: a wide gap between two tall nodes yields a larger-than-standard ghost.

* fix(canvas): fill more spots around the active node

The active-node lattice generated only 4 standard spots while the cap was 6, so
when no gap-fill custom filled the other two slots only 4 ghosts showed and the
free space around the node looked half-used. It now generates plenty of free
spots (nearest-first) and the accept pass keeps the closest `cap` of them — so
the recommendations fill the space around the active node (up to 6), still
hugging it (best spot is adjacent), with gap-fill customs where a standard panel
doesn't fit.

* refactor(canvas): rewrite recommendations as one minimal empty-rect pass

Replace the accreted machinery (clustering, islands, median sizing, lattice-BFS,
separate gap-fill, custom-preference ranking) with one clean idea:

- Find the ACTIVE node (focused if on screen, else the on-screen node nearest the
  viewport centre) — the "when active" logic stays separate.
- Look at the empty rectangles around it (findEmptyRects) and drop ONE panel into
  each: standard size where it fits, shrunk to fill the hole otherwise
  (individualized), hugged against the active node (aligned to its top-left,
  clamped into the rect).
- Empty canvas / panned-to-blank → a few standard spots where the user is looking.
- finalize() ranks nearest-first (on-screen first), dedupes, and accepts only
  non-overlapping spots.

placement.ts drops from 567 to 373 lines; removes rectGap, intersectionArea,
pointRectDistance, clusterNodes, boundsOf, gapFillCandidates, medianNodeSize.

* fix(canvas): hug the active node with a lattice instead of scattering

The empty-rectangle pass treated every nearby hole equally, so for a node inside
a dense grid the ghosts scattered across the whole cluster's perimeter — far from
the active node. Replace it with a lattice anchored on the active node's edges:
BFS outward (nearest-first) through free cells, each panel sized to fit its cell
(shrunk when a neighbour crowds it — individualized). Spots now hug the active
node and extend its grid instead of spreading. Removes findEmptyRects.

* fix(canvas): rebuild placement recs as a place-area grid scan

The old picker anchored a lattice to the active node and flooded outward
through free cells only — so a node boxed in on all four sides emptied the
queue and fell back to a single spot, and large open areas got one sparse
ghost. It also never distinguished a focused node from an unfocused canvas.

Replace the BFS with a full bounded scan of a panel-pitch lattice over a
place area:

- Focused node on screen → a tight area around it; recs hug the node and,
  because the scan visits every cell (not just reachable free ones), still
  reach the open space just beyond a surrounding ring.
- Nothing focused → the whole viewport, lattice phased to the on-screen node
  nearest the cursor → a wider, island-biased spread with more options.
- Pitch-spaced cells tile the free space densely without overlapping, and
  fitAt keeps the standard size where it fits, shrinking only to fill a
  tighter cell (individualized sizes, standard preferred).

Add tests for the boxed-in case (now ≥3 spots, not one fallback) and the
focus-vs-cursor differentiation.

* fix(canvas): fill gaps between staggered windows with a finer grid

The single node-phased lattice was too coarse: its cells aligned to one
reference node, so they landed in open areas or on top of a window, but
stepped right over the irregular gaps between staggered windows — and
nothing shrank, so every ghost was standard size.

Generate candidate positions from a grid woven from the reference lattice
(open areas) AND every nearby node's edges — the gap just right of / below
each window. fitAt then shrinks each spot to its actual gap, so smaller
holes get smaller, individually-sized ghosts (masonry) while roomy areas
still get standard panels. Holes below the minimum panel size stay empty.

* fix(canvas): keep the spot beside a node and stop far-flung recs

Two issues when a window was focused:

- No spot appeared directly beside the focused node. The hug anchor
  (node.edge + gap) was grid-snapped with round(), so a non-grid-aligned
  edge landed a few px too close — inside finalize's gap-1 clearance — and
  every spot on the node's own rows got dropped. Snap hug anchors AWAY from
  the node (ceil to the right/below, floor to the left/above) so the gap
  survives the snap.
- A recommendation showed up far from the focused node. Tighten the focused
  place area from 1.5 to 1 panel pitch so spots stay clustered around it
  (still reaches one pitch past a surrounding ring for the boxed-in case).

Add a regression test for the non-grid-aligned hug edge.

* fix(canvas): hide shadowed spots and make the grid type-independent

Two refinements to the placement picker:

- Occlusion: don't recommend a spot that sits behind a nearer one. A
  segment test from the ranking point (the active node, or the cursor)
  through each already-accepted spot drops candidates you'd have to look
  past another recommendation to reach.
- Consistent grid across panel types: lay spot POSITIONS on a canonical
  slot (the largest panel default) instead of the panel's own size, so the
  grid is the same whatever you're placing; each ghost is still sized to its
  own type, shrinking only to fit a tight gap. (A bigger panel still fits
  fewer spots in open areas — that part is geometric, not a bug.)

Add occlusion and per-type regression tests.

* fix(canvas): cluster focused recs near the node, no far gaps

Giant gaps came from two things: the canonical 800x600 slot made the grid
coarse and the place-area large, and a boxed-in focused node would scatter
spots into distant open space to fill the count of 6.

- Revert the oversized slot: lay the grid on the panel's own size again, so
  it's tight. (Positions stay consistent across types anyway because the
  hug ring is anchored to the node's edges, not the panel size.)
- Cap focused recs to within ~one panel-pitch of the node. An open node
  still gets a full ring of 6; a boxed-in one shows the few genuinely-near
  spots instead of far-flung ones.

Add a regression test that a node boxed on three sides keeps its recs near.

* fix(canvas): keep focused recs to one connected cluster touching the node

The previous one-pitch distance cap still let a spot land almost a full
panel-width away, which reads as a giant gap — especially a column hugging
some OTHER nearby window.

Replace the cap with a connectivity filter: seed with spots adjacent to the
focused node, then grow only through spots adjacent to each other (within ~one
gap). A spot that doesn't chain back to the node — because a neighbour window
sits between them — is dropped regardless of how near it ranks. Open nodes
still get a full ring; a blocked side yields nothing past the blocker rather
than a floater.

Add a regression test that nothing is recommended past a blocking neighbour.

* refactor(canvas): place recs by nearest-first free-space packing

The lattice + node-edge candidates + greedy non-overlap selection produced
odd gaps on non-grid layouts: it could leave empty space close to the node
unused while a spot landed farther out. No amount of capping/filtering fixed
the root cause, which was the candidate model itself.

Replace it with proper packing (MaxRects-style): decompose the free space
(place area minus the windows) into empty rectangles, then repeatedly drop one
ghost into the free spot CLOSEST to the ranking point, carve it (plus its gap)
out, and repeat. Because the nearest free space is always used first, no closer
empty spot is ever left unused — which is what removes the weird gaps — and the
result is mutually non-overlapping, tiles large areas, and shrinks a ghost to
fill a tight gap. Focused → packs around the node; unfocused → around the cursor.

Grid-snap each packed spot INTO its free rect so the gap survives snapping.
Drop the now-obsolete occlusion/connectivity machinery. Swap the stale occlusion
test for a packing-non-overlap test.

* refactor(canvas): dedupe free-rect pruning into one helper

* refactor(canvas): drop unused placement code

- PlacementCandidate.rank/onScreen were written but never read by the
  renderer or store (the ghost layer uses the array index, and on-screen-ness
  is only an internal ranking signal). Reduce the type to {point, size}; the
  two tests that read those fields now derive on-screen from the viewport.
- Delete layoutEngine.findFreePosition — a stale, name-colliding copy with
  zero callers (the live one lives in placement.ts).
@github-actions
github-actions Bot force-pushed the skills-index-update branch 8 times, most recently from aa831c2 to 7eadccb Compare June 16, 2026 11:34
@github-actions
github-actions Bot force-pushed the skills-index-update branch 8 times, most recently from f716923 to bcb7687 Compare June 24, 2026 09:52
@github-actions
github-actions Bot force-pushed the skills-index-update branch 8 times, most recently from 9588ad2 to 4e143db Compare July 2, 2026 09:32
@github-actions
github-actions Bot force-pushed the skills-index-update branch 5 times, most recently from 69226d8 to 3407431 Compare July 7, 2026 10:01
@github-actions
github-actions Bot force-pushed the skills-index-update branch 9 times, most recently from 8245a64 to 1e2b75d Compare July 19, 2026 08:33
@github-actions
github-actions Bot force-pushed the skills-index-update branch 7 times, most recently from 7a63daa to 2d2dd7e Compare July 26, 2026 08:40
@github-actions
github-actions Bot force-pushed the skills-index-update branch 8 times, most recently from e3ed8a7 to 48d521b Compare August 3, 2026 10:06
@github-actions
github-actions Bot force-pushed the skills-index-update branch 4 times, most recently from f432084 to 125a5ec Compare August 7, 2026 07:41
@github-actions
github-actions Bot force-pushed the skills-index-update branch from 125a5ec to ce7be8a Compare August 8, 2026 07:14
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.

0 participants