Skip to content

Commit 0b5bc4f

Browse files
icecrasher321claude
andcommitted
fix(canvas): paint the running hatch once across the row, not per slot
The bars came out bunched in some places and spread in others. Per-slot backgrounds cannot avoid that: each button starts its own gradient at its own origin, so the phase resets at every slot — and the end slots are 40px against the others' 24px, so the resets are not even uniform. Three passes of tuning the stops were all chasing a constraint the approach could not satisfy. The hatch is now one element spanning the row, so there is one gradient and one phase. It sits behind the buttons and grows by width: the run/stop button keeps an opaque fill while running and masks the part growing underneath it, and every other slot is transparent mid-sweep so the hatch reads through. The slots no longer paint anything themselves, and the per-slot filled flag goes with them. `--surface-2` is unchanged; only where it is painted moved. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent aa695c8 commit 0b5bc4f

1 file changed

Lines changed: 41 additions & 30 deletions

File tree

  • apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/action-bar/action-bar.tsx

Lines changed: 41 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,22 @@ const ACTION_BUTTON_STYLES = [
4545
].join(' ')
4646

4747
/**
48-
* The hatch a filled sweep slot paints: narrow uprights leaning right, so a
49-
* filling run reads as one continuous `////` band.
48+
* The running hatch: narrow uprights leaning right, painted ONCE across the row.
5049
*
51-
* Repeating, not one bar per slot. A single bar left the rest of its 24px button
52-
* empty, so the marks inherited the button grid's rhythm and sat ~19px apart —
53-
* the row read as isolated ticks rather than a loader. The pitch instead divides
54-
* the row's own rhythm: slot + `gap-[2px]` is 26px, so a 13px horizontal pitch
55-
* puts exactly two bars in every slot and stays in phase across the gaps,
56-
* including the 40px end slots. Bars land every 13px with a uniform 6px between
57-
* them, whatever the run's length.
50+
* Per-slot backgrounds cannot do this. Each button starts its own gradient at
51+
* its own origin, so the phase resets at every slot — and the end slots are 40px
52+
* against the others' 24px, so the resets are not even uniform. The row came out
53+
* as bars bunched in some places and spread in others. One element spanning the
54+
* row has one gradient, and therefore one phase.
5855
*
59-
* Stops are measured along the 105° axis, not horizontally, so they carry the
60-
* `sin(105°)` factor: a 7px bar on a 13px pitch is 6.76px on a 12.56px period.
61-
* `bg-clip-content` with symmetric padding sets the height without touching the
62-
* slot's size, so the swell measured around it does not move. `--surface-2` is
63-
* untouched.
64-
*
65-
* Each variant is spelled out: Tailwind's JIT reads literal class strings.
56+
* `--surface-2` is the same fill the slots used; only where it is painted moved.
6657
*/
67-
const RUNNING_SWEEP_FILL = [
68-
'!bg-[repeating-linear-gradient(105deg,var(--surface-2)_0_6.76px,transparent_6.76px_12.56px)]',
69-
'hover-hover:!bg-[repeating-linear-gradient(105deg,var(--surface-2)_0_6.76px,transparent_6.76px_12.56px)]',
70-
'!bg-clip-content !py-[4px] !rounded-none',
71-
].join(' ')
58+
const RUNNING_HATCH =
59+
'bg-[repeating-linear-gradient(105deg,var(--surface-2)_0_5.8px,transparent_5.8px_11.6px)]'
7260

73-
const RUNNING_SWEEP_FILL_STATIC = [
74-
'motion-reduce:!bg-[repeating-linear-gradient(105deg,var(--surface-2)_0_6.76px,transparent_6.76px_12.56px)]',
75-
'motion-reduce:!bg-clip-content motion-reduce:!py-[4px] motion-reduce:!rounded-none',
76-
].join(' ')
61+
/** Left edge of the hatch: clears the run/stop button, which stays live mid-run. */
62+
const RUNNING_HATCH_INSET_SWELL = 'left-[42px]'
63+
const RUNNING_HATCH_INSET_PLAIN = 'left-[26px]'
7764

7865
const ICON_SIZE = 'size-[14px]'
7966

@@ -291,7 +278,6 @@ export const ActionBar = memo(
291278
const getActionButtonStyles = (actionId: ActionId) => {
292279
const runningSweepIndex = runningSweepActionIds.indexOf(actionId)
293280
const isRunningSweepSlot = isSweeping && runningSweepIndex >= 0
294-
const isRunningSweepFilled = isRunningSweepSlot && runningSweepIndex < runningSweepFilledCount
295281

296282
return cn(
297283
actionButtonStyles,
@@ -328,11 +314,11 @@ export const ActionBar = memo(
328314
]
329315
: 'hover-hover:!bg-transparent dark:hover-hover:!bg-transparent',
330316
],
317+
/* The hatch is painted across the row, not per slot — a slot only clears
318+
itself out of its way. */
331319
isRunningSweepSlot && [
332320
'!opacity-100 [&_svg]:!opacity-0',
333-
isRunningSweepFilled ? RUNNING_SWEEP_FILL : '!bg-transparent hover-hover:!bg-transparent',
334-
RUNNING_SWEEP_FILL_STATIC,
335-
'motion-reduce:transition-none',
321+
'!bg-transparent hover-hover:!bg-transparent',
336322
],
337323
/* `!` is required: these buttons are also `disabled` when locked, and
338324
the emcn Button base carries `disabled:opacity-70`, which outranks a
@@ -379,13 +365,38 @@ export const ActionBar = memo(
379365
>
380366
<div
381367
className={cn(
382-
'flex flex-row items-center gap-[2px]',
368+
'relative flex flex-row items-center gap-[2px]',
383369
isSwell && [
384370
'pointer-events-none h-full opacity-0 transition-opacity duration-[30ms] [transition-timing-function:cubic-bezier(0.23,1,0.32,1)]',
385371
'group-data-[action-menu-ready]:pointer-events-auto group-data-[action-menu-ready]:opacity-100 group-data-[action-menu-ready]:duration-100',
386372
]
387373
)}
388374
>
375+
{/*
376+
Painted behind the row rather than into each slot, so the hatch has a
377+
single gradient and a single phase. The run/stop button keeps an
378+
opaque fill while running, which masks the hatch growing underneath
379+
it; every other slot is transparent mid-sweep, so it shows through.
380+
*/}
381+
{isSweeping && (
382+
<span
383+
aria-hidden='true'
384+
className={cn(
385+
'pointer-events-none absolute inset-y-[4px] right-0 overflow-hidden',
386+
isSwell ? RUNNING_HATCH_INSET_SWELL : RUNNING_HATCH_INSET_PLAIN
387+
)}
388+
>
389+
<span
390+
className={cn(
391+
'block h-full transition-[width] duration-150 ease-linear motion-reduce:transition-none',
392+
RUNNING_HATCH
393+
)}
394+
style={{
395+
width: `${(runningSweepFilledCount / runningSweepActionIds.length) * 100}%`,
396+
}}
397+
/>
398+
</span>
399+
)}
389400
{!isNoteBlock && (!isInsideSubflow || isWorkflowRunning) && (
390401
<Tooltip.Root preferAbove>
391402
<Tooltip.Trigger asChild>

0 commit comments

Comments
 (0)