misc updates - #306
Merged
Merged
Conversation
Forward port of the snapping half of cocalc#8816 (plus the follow-up 28bf57adf4 icon tweak) from sagemathinc/cocalc onto cocalc-ai. ## Snap engine - `snap.ts`: computes snap targets from the page/slide border, the edges and center axes of the other elements on the page, and the background grid; returns the adjusted offset plus the guide lines to render. - `snap-guides.tsx`: renders those guide lines on the canvas while a drag is in progress. - Integrated for both focused (edit-mode) and unfocused (select-mode) dragging; guide state lives in `canvas.tsx` and is cleared on unmount. - Grid snapping uses the major grid (100px) at any zoom and the minor grid (20px) from 200% zoom on. The grid constants now live in `elements/grid.tsx` as the single source of truth. - Toolbar toggle (`snapToAlignment` in the frame desc, default on) with a shift-key override to bypass snapping for a single drag. ## Page geometry - The CSS background grid is anchored to the data-space origin (0,0), so grid lines sit at stable positions that actually match the snap targets instead of drifting with the canvas extent. - `fitToScreen` uses the screen-pixel viewport directly instead of `getViewportData()`, which divides by the current scale. The old code also multiplied by `canvasScale` on top of that, so repeated calls oscillated instead of converging. - The zoom-reset button now fits content to the screen rather than jumping to a fixed 100%. The Jupyter cell height and ctrl+wheel zoom parts of #8816 are handled separately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Forward port of the Jupyter-cell half of cocalc#8816, which also folds in the height work from cocalc#8780 that never reached this fork. ## The bug Cell height was derived from `divRef.getBoundingClientRect().height / canvasScale + EXTRA_HEIGHT`, i.e. a rendered pixel height divided by the canvas scale plus a hardcoded 30px fudge factor. That is wrong at every zoom level other than 100%, and the fudge factor was covering for a real measurement error: the InputPrompt's `margin-top` collapses through the unstyled inner div, so `divRef.scrollHeight` silently omits it. ## The fix - Measure from `outerRef.scrollHeight` plus the outer border. `outerRef` has padding, which blocks margin collapse, so the prompt's margin is included. No scale division and no fudge constant. - `measureHeightInner` (inner div + padding + border) is the fallback used to detect shrink while focused, where `outerRef` has `height: 100%` and therefore cannot report a height below `element.h`. - The three separate measurement effects (focused resize observer, resize trigger, unfocused re-measure on blur) collapse into one effect with a single ResizeObserver watching both the outer and the inner div, so focused and unfocused cells go through the same code path. - Grow immediately, shrink debounced by 250ms, and only shrink when unfocused -- shrinking while focused oscillates. - `element.h` is read through a ref so the observer callback never acts on a stale height. - A `requestAnimationFrame` re-measure catches children that lay out after the first frame (CodeMirror, output rendering); the observer detaches after 5s for unfocused cells unless a computation is still streaming. - Unfocused cells render with `height: auto` so the box can actually track content; focused cells keep `height: 100%` with `overflowY: hidden`. - The control bar is anchored with `bottom: calc(100% + 10px)` instead of a hardcoded `top: -34px`, so it cannot overlap the cell when its own height changes. ## box-sizing `getStyle` sets `boxSizing: "border-box"` (the one line #8780 added to `style.ts`). The measurement above depends on it. As content-box, the outer div's `height: 100%` resolves to `element.h` for the content box with the 5px padding and border outside it, so `outer.scrollHeight` reports at least `element.h + 10`; adding the border makes every measurement exceed the current height, the grow branch fires, the ResizeObserver sees the taller box and measures larger again. The cell then expands without bound as soon as it is inserted. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deliberate deviation from upstream cocalc#8816, which grows on any increase (`h > element.h`) while requiring a >2px delta to shrink. That asymmetry is what let the box-sizing bug run away instead of settling: a constant per-cycle overshoot in the measurement always satisfied the grow condition, the ResizeObserver saw the taller box, and the cell expanded without bound. The overshoot itself is fixed, but the missing threshold is what turned a static offset into an infinite loop. Making the grow side symmetric with the shrink side means any future measurement error costs at most a couple of pixels instead of an unbounded loop. Growth stays immediate for real content changes, which are far larger than the threshold. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Forward port of the ctrl+wheel-zoom half of cocalc#8816 together with its follow-up cocalc#8823 (the PR actually carrying the PR-TODO-cocalc2 label). ## The bug Two handlers were fighting over the same events. Chrome translates ctrl+wheel into pinch gesture events, so `usePinch` saw them at the same time as `useWheel` did, and the resulting zoom was erratic. The `useWheel` path was also disabled outright on macOS to work around that conflict, and it scaled linearly, so a scroll tick moved zoom by a fixed number of font size units -- a huge relative jump when zoomed out, a barely visible one when zoomed in. ## The fix - Replace `useWheel` with a native `wheel` listener registered in capture phase, which calls `stopImmediatePropagation` so the event never reaches `@use-gesture`. `usePinch` is now touch-pinch only, and the macOS exclusion is gone. - Multiplicative (log2) scaling, so equal scroll ticks produce equal percentage changes at any zoom level, with the per-event exponent capped so a fast scroll cannot jump across the range. - Font size is tracked in a ref during a gesture, so throttled store updates cannot feed a stale value back into the next event. - `getFontSize` and the save callback are read through refs, removing the stale-closure hazard in the pinch `from()` callback. - New `wheelSpeed` option; the whiteboard canvas passes 2, which restores the faster zoom that felt right there. - The PDF viewer always supplies `getFontSize` now, falling back to `font_size` when no external `zoom` is provided. Previously it only supplied it in the `zoom != null` case, so the wheel handler had no starting value and ctrl+wheel did nothing in the PDF frame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…oggle Addresses two review findings on the snap work. ## Snap tolerance was in data coordinates `SNAP_THRESHOLD = 8` was compared against data-space distances, while `canvasScale` was only consulted to choose the grid step. The tolerance was therefore 2 screen pixels at 25% zoom and 32 at 400% -- snapping was almost unreachable zoomed out and unpleasantly sticky zoomed in. The constant is now documented as screen pixels and divided by `canvasScale` at use, via a `snapThreshold()` helper that also guards against a missing or non-positive scale. ## Snap toggle was not exposed to assistive tech The button contained only an `aria-hidden` icon, so it had no accessible name, and its on/off state was conveyed by background colour alone. Added `aria-label` and `aria-pressed`, per `src/.agents/accessibility.md` which requires icon-only controls to carry a real name rather than lean on a tooltip. The inline `#fff` is replaced by a named `SELECTED_FG` beside `SELECTED` in `tools/common.tsx`. `ToolButton` above still has its own inline `#fff`; that is pre-existing and left alone rather than widening this diff. ## Tests `snap.test.ts` covers threshold scaling across zoom levels, exact alignment and guide emission, page-border snapping, and the major/minor grid transition at 200%. `tools/panel.test.tsx` queries the toggle by accessible role and name and asserts `aria-pressed` in both states plus focus and activation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses a review finding: unfocused cells could grow but never shrink.
`position.tsx` renders every element into a parent with a hard
`height: ${element.h}px`, and the unfocused code element carries
`minHeight: 100%`. `outer.scrollHeight` is therefore floored at
`element.h` and `measureHeight()` can never report a smaller box, so the
shrink comparison was dead code -- a cell that lost output stayed at its
old height forever.
Shrink is now measured from the inner div, which is `height: auto` and so
tracks content. That is what the pre-#8816 code did, with a comment saying
exactly why; #8816 moved unfocused measurement to the outer div and lost
it. The reason it moved was margin collapse: the InputPrompt's margin-top
escaped the unstyled inner div, making `inner.scrollHeight` underestimate.
That is fixed at the source instead -- the inner div now has
`display: flow-root`, establishing a block formatting context so the
margin is contained and counted. Outer padding already blocked the margin
from escaping the outer box, so this changes measurement only, not layout.
Grow still measures the outer div, where the floor is harmless.
`height.test.tsx` covers shrink, the commit flag for collaborators, the
2px deadband in both directions, immediate growth, and the MIN_HEIGHT
clamp. Three of its six cases fail against the previous implementation.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses a review finding: the native ctrl+wheel handler treated `deltaY` as pixels unconditionally. Browsers may report wheel deltas in lines (`deltaMode` 1) or pages (`deltaMode` 2) rather than pixels. Firefox and several mouse drivers report roughly +-3 lines per notch, so the exponent was computed from 3 instead of ~100 and zoom moved by about 0.3% per notch -- effectively inert, while the same mouse in Chrome worked normally. Line and page deltas are now scaled to approximate pixels before the exponent is computed, so a notch feels the same regardless of how the browser reports it. `normalizeWheelDeltaY` is exported and unit tested, including that a Chrome pixel notch and a Firefox line notch land within the same order of magnitude, and that sign is preserved in every mode so zoom direction never inverts. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Two review findings on the guide lifecycle. ## Guides vanished at exact alignment Guide emission was gated on `dx !== 0` / `dy !== 0`, but dx is zero exactly when the element is already perfectly aligned -- so the guide disappeared at the moment it was most useful, and reappeared as soon as you drifted off again. Gate on whether a target actually matched instead. When nothing is within tolerance the matched lists are empty, so out-of-range drags still emit nothing. ## Stale guides after a null drag `not-focused.tsx` cleared the guides inside `if (data.x || data.y)`. Dragging an element away and back to its exact starting position takes the else branch, which treats the gesture as a click, so the guides were left on screen. Clear them unconditionally at the top of `onStop`. The same component was also missing the unmount cleanup that `focused.tsx` already has, so guides could survive a component teardown mid-drag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review finding: `saveThrottled` and `save` were built by `useMemo` keyed only on the frame `id`, while closing over `disabled`, `onZoom`, `actions` and `throttleMs`. A hook that first mounted with `disabled: true` therefore kept a no-op save forever. Once it was enabled again the wheel listener was installed and fired, but every zoom went into the discarded closure -- zoom appeared dead with no error. A changed `onZoom` was ignored for the same reason, and a changed `throttleMs` never took effect. Those values are now read through refs, so the throttled function stays stable without going stale, and the memo is keyed on `[id, throttleMs]`. The `disabled` check moved inside the callback. A cleanup effect cancels the throttle when it is replaced or the hook unmounts, so a pending trailing call cannot land after teardown. `pinch-to-zoom-hook.test.tsx` drives real ctrl+wheel events through the hook and covers enabled, disabled, disabled → enabled, a swapped onZoom, and enabled → disabled. The disabled → enabled case fails against the previous implementation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Review finding: moving the `#fff` literal from `panel.tsx` into a `SELECTED_FG` constant relocated it rather than removing it. The repo rule is to take colors from `COLORS`. There was no white token, so this adds `WHITE` to `MAIN_COLORS` and points `SELECTED_FG` at it. Note this touches `util/theme.ts`, which widens the CI matrix for the PR. `ToolButton` in `panel.tsx` still carries its own inline `#fff`; that is pre-existing and left for a separate cleanup rather than widening this change set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Follow-up to 763fec9, which moved the guide clear to the top of `onStop` so the null-drag branch would reach it. That was the wrong end: `computeSnapForDrag` runs later in the same handler and calls `setSnapLines(result.lines)` for the drop position, so React's batched update ended with the guides set. Every snapped, non-zero drop left them on screen -- worse than the null-drag case it was fixing. The clear now runs last, after the computation and the move, and outside the if/else so both branches reach it. `not-focused.test.tsx` drives Draggable's callbacks directly (jsdom cannot produce a real drag) and asserts the final `setSnapLines` call is the clear, for a normal drop and for a drag returning to its exact starting position, plus the unmount cleanup. The normal-drop case fails against the previous ordering. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses three Codex review findings on the snap engine. ## Guides could point at a line nothing snapped to Tie collection compared only absolute distances while the applied offset kept its sign, so two targets equidistant on opposite sides both produced a guide although only one was used. Candidates now carry their signed adjustment, and guides are filtered to those matching the adjustment actually applied. ## Duplicate targets made dragging quadratic Every element sharing an edge or centre coordinate contributed its own matched entry, and each entry then called an extent helper that scans all other elements -- so a board with many aligned elements did that scan repeatedly per drag event and rendered a stack of identical guide divs. Matched targets are now deduplicated by coordinate, so each guide line is computed and drawn once. ## Multi-selection snapped against inflated bounds For a multi-selection the moving element is the synthetic "selection" rect from canvas.tsx, whose w/h are `xMax - xMin + 1` to give the selection border somewhere to draw. Snapping against that left right/bottom alignment a unit short and centre alignment half a unit off. The padding is removed again before computing the snap. ## Grid targets only where a grid is drawn `<Grid>` renders only for `mainFrameType == "whiteboard"`, but grid targets were added whenever a scale was supplied, so slides snapped to invisible 100/20-unit lines competing with the page border, page centre and element targets that alignment on a slide is actually about. `computeSnap` takes `gridEnabled`, derived in canvas.tsx from the same condition that renders the grid. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The arrowhead sat past the end of the line: the line was drawn as a full `border` box spanning the whole distance, with an `Icon` caret overlaid at a negative offset, so the line ran on underneath the head and the arrow read as too long. Ports the #8780 rendering: the head is an SVG triangle sized from `arrowSize`, and the line carries `marginRight: tipLength` so it stops where the head begins. Selection uses `outline` rather than `border` so the indicator cannot shift the rotation pivot, and the click target is an explicit invisible strip instead of padding on the rotated container -- padding was distorting the geometry it was attached to. Endpoint selection moves to `getEdgeEndpoints` (also from #8780), which picks sides by center-to-center ray analysis weighted by each rect's aspect ratio: vertically stacked elements connect bottom-to-top, and horizontal ones right-to-left, instead of the previous nearest-midpoint heuristic that could pick a side across a corner. `edge-endpoints.test.ts` covers the four cardinal arrangements, the aspect-ratio weighting, coincident centers, and zero-size rects. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Clicking a text box grew it and left an empty strip above the text. `modeSwitchStyle` passed `top: -82px; left: -18px` but no `position`, and the wrapper in `editors/markdown-input/mode-switch.tsx` is `position: relative`. Relative offsets move an element visually without releasing its place in normal flow, so the control appeared outside the box -- which is why it looked correctly placed -- while still holding a strip of empty space where it used to sit. The hardcoded `+ 15` in the height calculation was compensating for that reserved space. Both go: the mode switch becomes `position: absolute` (as in cocalc#8780, which is what took it out of flow upstream), and the `+ 15` term is dropped. Upstream kept `+ 15` and added a blur handler that restores the pre-edit height instead; dropping it outright is a deliberate divergence, since with the control out of flow there is nothing left for it to reserve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The rendered text container is `width: 100%` with `padding: 5px` and was content-box, so it laid out 2*PADDING wider than the element it lives in: measured live, an 847px text element rendered a 857px inner div, pushing the paragraph 5px past the element's right edge. The editor's container inset the text by 5px instead, so switching between the two modes shifted the text horizontally by 10px. Both containers are now border-box, so PADDING sits inside the element width in either mode. Measured against the running Lite instance afterwards: left and right gaps are 5px in both modes, where before the rendered view overflowed by 5px on the right. Upstream has the same bug; this is not a port regression. A vertical mismatch remains and is deliberately not addressed here: the rendered paragraph carries markdown's 1em top margin (29px total from the element top) while the editor's paragraph has none (4.9px). Fixing that properly means reconciling the slate editor's block margins with the static markdown CSS, which is shared well beyond the whiteboard. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… edges Three geometry defects in the arrow renderer, all inherited from the cocalc#8780 code this was ported from. ## Thick stems hung below the endpoint axis The endpoints lie on y=0 of the rotated frame, but the stem was a bare `borderTop`, which hangs entirely downward from its element's top edge. At thickness 1 the half-pixel offset is invisible; at the supported maximum of 30 the line sits 15px off-axis -- and the head, which was centred on y=0, no longer met the stem. That is the "arrow head against the stem" mismatch. The stem is now an absolutely positioned child offset by half its thickness, so line and head straddle the same axis at any thickness. ## Head was drawn at opacity squared The shared parent carried `opacity` and the polygon carried it again, so `opacity: 0.5` rendered a 0.25 head against a 0.5 line. The polygon's own opacity is removed; the parent applies it once to both. ## Head overshot short edges `tipLength` was a fixed fraction of `arrowSize`, so for an edge shorter than the head the stem's `marginRight` exceeded the available width, the line collapsed to nothing, and the head extended past the element it points at -- e.g. a 13px head on a 5px gap. Head length and width now scale down together once the edge is shorter than the head, preserving its proportions, and it is omitted entirely at zero length. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Addresses the Codex accessibility finding on the replaced edge hit target. The selection surface was a plain `div` carrying only `onClick`, so an edge could be selected by pointer but not reached by Tab or activated with Enter/Space. AGENTS.md requires focused accessibility coverage for changed interactive UI, and this hit target was changed by the arrow rewrite. It is now a real `<button type="button">` with an accessible name (`ariaLabel`, defaulting to "Edge"), so keyboard activation comes from the platform rather than hand-rolled key handling. It stays visually transparent, and the browser's focus ring is deliberately not suppressed so the focused edge is visible. `onClick` moved off the outer container onto the button. The container renders at zero height -- its children are absolutely positioned -- so the 20px strip was already the real hit area, and having the handler in one place means a click cannot fire twice. Tests query by role and accessible name per `src/.agents/accessibility.md`, covering the default and caller-supplied name, focusability, activation, that the focus ring is not suppressed, and that no control is rendered for a non-clickable edge. Note this makes edges the only keyboard-reachable whiteboard elements; text, code and notes remain pointer-only. A coherent canvas-wide selection and traversal model is still needed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…give it a chisel nib The live preview of a pen stroke was drawn incrementally: on every mousemove the last two points were stroked onto the preview canvas. With a translucent pen (the highlighter, opacity 0.4) the overlapping round ends of consecutive segments each composited separately, so the stroke built up to fully opaque while drawing and then snapped back to translucent the moment the mouse was released and the real element rendered. The preview now redraws the whole path in one stroke/fill, coalesced through `requestAnimationFrame`, and `drawCurve` always paints the curve in a single canvas operation. Points closer than one pixel to the previous one are dropped, so a high frequency pointing device does not blow up the path. Also fixed along the way: - `Pen` cleared its canvas *after* translating by `pad`, so a `pad` wide strip along the top and left was never cleared. That strip holds the top half of a wide stroke, so every re-render of a translucent stroke composited it onto itself and the stroke got darker (0.4 -> 0.64 -> 0.78 ...) as the board was edited. Clearing now happens with the identity transform. - The preview canvas had no explicit CSS size, so it was laid out at its bitmap size; the old code compensated by drawing at 1/devicePixelRatio. The canvas is now sized in CSS pixels and drawn at full device resolution, which also makes the preview line up with the finished element on HiDPI displays. - `drawCurve` sets `globalAlpha` unconditionally instead of only when an opacity is given, so a translucent stroke can no longer leak its alpha into whatever is drawn next on the same context. The highlighter preset now uses `nib: "chisel"`: an upright rectangle 2*radius tall and a fraction of that wide, swept along the path, which is how a real highlighter behaves -- a wide band when swept horizontally, a thin line when moved vertically. The stroke is the union of the nib's positions, emitted as one path with consistently oriented subpaths and filled once. Elements without `nib` keep the old round pen rendering, so existing drawings are unchanged.
Fail recipient creation closed until the Zendesk webhook and opt-out secret is configured. This prevents a draft from retaining a deterministic fallback token and later becoming deliverable after configuration changes.
Translate app-server exits reported as status 137 or SIGKILL into an actionable user-facing error that identifies project RAM exhaustion as the usual cause and suggests increasing RAM or reducing memory use. Preserve existing diagnostics for all other exit statuses and cover both Node process exit representations.
Wait for an older locked project outbox row instead of allowing newer rows to overtake it. The collaborator projector can briefly hold project creation events; skipping those rows allowed a later stale creation payload to overwrite an already projected host and state. Add unit and PGlite coverage for the locking contract and the observed causal-order regression.
Await course folder creation before dismissing the assignment selector or modal. Keep the controls open and show a local error when the project operation fails, while disabling repeat submissions until it settles. Propagate assignment mkdir failures to the caller and cover the selector's success and failure behavior with accessible interaction tests.
Add seed-global immutable PDF storage for purchase orders, including digest verification, optimistic concurrency, idempotent upload, retained voiding, and audited downloads. Expose the complete workflow through fresh-authenticated Conat RPCs, preview-first CLI commands, the AR detail UI, and admin documentation. Keep document bytes out of ordinary order payloads, preserve late-arriving procurement evidence after completion, and let a reviewed PO reference fill an otherwise empty order PO number. Correct nested quote downloads to use --output-file so they do not collide with the root CLI output option. Document a separate Stripe-native Quotes rollout that retains CoCalc commercial authority and local document history while accounting for Stripe Invoicing Plus requirements.
Load the current membership tier catalog when the account storage warning is opened and compare each purchasable tier with the account's effective storage caps. Show total and per-project storage, headroom at current measured usage, annual pricing, and calculated annual savings while excluding tiers that would not improve both existing limits. Allow users to enter the existing membership purchase flow directly from a suitable tier, with annual billing selected, and add accessible rendering and comparison coverage.
…ordering Harden project projection and support-facing workflows
Carry the selected storage upgrade tier's available billing interval into the membership purchase modal. Prefer annual billing when the tier offers it, but use monthly billing for monthly-only tiers instead of requesting an unavailable annual quote. Cover both annual and monthly-only option selection.
Persist an authoritative guidance delivery timestamp after the live Codex turn accepts a steer request, including requests routed to another ACP worker. Preserve that state while removing the queued projection, and use the delivery time to interleave guidance with agent activity instead of its earlier composition time. Include delivery time in virtualized chat row revisions so mounted activity logs update when the authoritative timestamp arrives.
…ordering Handle monthly-only storage upgrade tiers
Override the archived image-size package used by Metro with the audited image-size-next 1.2.2 compatibility release, fixing CVE-2025-71329 and CVE-2025-71330. Keep the override scoped to vulnerable versions so a future upstream resolution remains unaffected.
Use the OIDC-published Patchflow 0.8.1 release in the sync and backend consumers. The release updates immutable to its patched version and removes the obsolete save-dev placeholder from Patchflow's dependency graph.
Contributor
Author
|
lgtm |
Make alignment offsets visible during dragging before committing the same snapped position on drop, and exclude invisible frame contents from both snap targets and guide extents. Keep completed, unfocused Jupyter cells observed so delayed and collaborative content changes continue to correct their saved height. Prevent visible arrow geometry from intercepting the accessible selection button. Add focused regressions for each behavior, including snap preview position and resize notifications after the previous five-second cutoff.
Render active whiteboard code-cell output from the Jupyter run overlay instead of waiting for the final durable notebook update. Keep the output component mounted while a cell is running so early stream messages have a render target, while persisting only the completed result to the collaborative board document. Add focused coverage for the running-without-output state and overlay forwarding.
Replace rotated HTML edge lines with side-aware SVG cubic Bezier paths and target markers. Render completed edges below both endpoint elements, use the curve itself for selection and keyboard focus, and retain a wide transparent hit path for pointer selection without changing persisted board data. Extend endpoint geometry with attachment sides and cover geometry, z-ordering, previews, marker sizing, and accessible interaction.
Move delete and insert controls into a dedicated row beneath each page thumbnail so the page-strip scrollbar cannot cover them. Constrain page items with border-box sizing, reclaim the former button gutter for the preview, and prevent insert clicks from bubbling into page selection. Give both controls explicit accessible names and cover the layout and click isolation with focused tests.
Place the tooltip outside the page-delete Popconfirm so the confirmation trigger reaches its button. The previous nesting swallowed Popconfirm's injected trigger props, leaving the trash control inert. Cover opening the confirmation and invoking deletion only after explicit confirmation.
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.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.