Skip to content

FEAT: Pick an operation from existing values in the labels bar - #2396

Open
varunj-msft wants to merge 9 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/12437-Allow-Selecting-An-Operation
Open

FEAT: Pick an operation from existing values in the labels bar#2396
varunj-msft wants to merge 9 commits into
microsoft:mainfrom
varunj-msft:varunj-msft/12437-Allow-Selecting-An-Operation

Conversation

@varunj-msft

Copy link
Copy Markdown
Contributor

Description

Setting the operation label meant retyping the name from memory. There was a suggestion list meant to help, but it never appeared: it filtered the known values against the value you were replacing, and with the shipped default op_trash_panda that matches nothing.

Clicking the operation label now opens a combobox listing the operations already in memory, using the labels request the bar was already making. Typing filters the list, and offers to create a name that doesn't exist yet.

A few details worth calling out:

  • Existing values are selectable as-is; only new names get validated. Operations created before the current naming rules (like some-op-name) stay usable.
  • The editor now renders in the labels popover too. Clicking a label there used to set edit state but render no editor at all, so on a narrow window — where the chip doesn't fit inline — there was no way to edit it.
  • Label rows are reachable by keyboard, and focus moves into the picker.
  • Escape, clicking away, and Tab all leave without writing a value.

The operation you pick applies to attacks you start afterwards; it doesn't relabel existing ones. It also isn't persisted across a page refresh — that's a separate question about where per-user GUI state should live, so I left it out rather than guess.

No backend or API changes.

Tests and Documentation

21 new tests in LabelsBar.test.tsx covering the picker: options listed on open without clearing the field, filtering, creating a new name, rejecting an invalid one, selecting a legacy value that predates the naming rules, loading and empty states, keyboard select/dismiss, and editing from the popover. Existing label behavior is unchanged and still covered.

  • npx jest src/components/Labels src/components/Home src/App.test.tsx → 113 passing
  • npm run lint and npm run type-check → clean
  • Coverage on LabelsBar.tsx goes up (branches 82.65 → 85.81, lines 89.65 → 92.85)

Docs: added a short paragraph to doc/gui/0_gui.md describing the picker and the "applies to new attacks only" behavior. It's a plain markdown page with no paired notebook, so JupyText doesn't apply here.

Setting the operation label meant retyping the name from memory. The
suggestion list that was supposed to help never appeared, because it
filtered the known values against the value being replaced -- with the
shipped default op_trash_panda that matches nothing.

Editing the operation label now opens a combobox listing the operations
already in memory, sourced from the labels request the bar already makes.
Typing filters the list and offers to create a name that doesn't exist yet.

- Existing values are selectable as-is; only new names are validated, so
  operations created before the current naming rules stay usable.
- The editor now renders in the labels popover too. Clicking a label there
  previously set edit state but rendered no editor, which showed nothing at
  all when the chip was too narrow to fit inline.
- Label rows are reachable by keyboard, and focus moves into the picker.
- Escape, clicking away, and Tab all leave without writing a value.

The selected operation applies to attacks started afterwards; it does not
relabel existing ones.
@romanlutz

Copy link
Copy Markdown
Contributor

Screenshots please! Or video.

The dropdown took its width from the input it hangs off, so longer
operation names were cut off mid-name with no ellipsis --
op_2026_05_mai_image_2.5 rendered as op_2026_05_mai_image. Names never
wrap out of it either: values may only contain letters, digits and
underscores, none of which are line break opportunities.

Widening the input instead would push it past the labels bar and clip
the control itself, so leave the input alone and let the dropdown size
to its own content.
Rejecting a bad name after the fact put the reason in a line of text
beside the picker, and the labels bar clips anything that overflows it.
On a narrow ribbon none of it survived; at full width it read "Only l".
The message also stayed on screen while the name was corrected.

The rules are now stated in the dropdown as the name is typed, and a
name that breaks them is not offered for creation at all. The dropdown
sizes to its contents, so the whole message is always readable.

Fluent dims disabled options to roughly 1.9:1 against their background,
which is too faint for text that has to be read rather than chosen, so
the notes carry their own colour.
With more operations than fit under the input -- and there are already
around sixty in use -- the list stopped hanging off the picker and became
a full height column pinned to the top of the window, covering the page.
Giving it a ceiling lets it stay where it belongs and scroll instead.

A name typed into the picker also disappeared from it. Operations are
read once from the labels API, and a name only reaches that API after an
attack has been stored under it, so a name created moments earlier was
absent when the picker was reopened and was offered for creation again.
Newly created names now join the list they came from.
A failed labels request left the picker looking like a working picker
with nothing in it, so someone whose backend had hiccuped was told their
operations did not exist and invited to type a name that already existed
somewhere else. It now says it could not load them, and keeps the "none
recorded yet" wording for the case where that is actually true.

Also fold the operation naming rules back into the single validator they
were copied from, and stop the newly created name from being appended to
a list captured before the request that fills it had returned.
The picker lets a name be typed and created before the request that
fills it has come back, and the response then replaced everything that
had been collected in the meantime, so a name created during those first
moments disappeared again as soon as the list arrived.

The response is now merged with what is already there rather than
replacing it.
The 240px cap never took effect. Fluent's combobox defaults to
autoSize: true, which writes its own max-height inline once positioned,
and an inline style beats the class the cap lives in. Overriding only
matchTargetSize left autoSize in place, so the list stretched to whatever
room it had: 501px below the input at an 800px viewport, and above the
input it ran to the top of the window.

Asking Fluent to auto-size width alone leaves the height to the class.
Measured in Chromium with 60 options: 240px and anchored under the input
at 800px, 240px and anchored above it at 500px and 420px, still scrolling
internally, and the dropdown width is unchanged. It shrinks below the cap
when there are fewer options.

No test: jsdom does not position the popup, so the inline max-height that
caused this is never written there and a unit assertion would pass either
way.
Sizing the dropdown to width alone hands the height back to the class,
which is what makes the 240px cap work, but it also gives up Fluent's
vertical fitting. The cap was a flat 240px, so in a window shorter than
about 250px the list ran past the viewport edge and the options there
were unreachable.

Yielding to the viewport keeps both: measured with 60 options, the list
is still the full 240px and anchored at every height from 300px up, and
at 200px it now renders 168px and stays on screen instead of overflowing
by 40px.
Every sizing bug in this feature shipped past a green unit run, because
jsdom has no layout engine: reverting the fix that caps the list height
leaves all 53 LabelsBar unit tests passing.

These run in the existing mock Playwright project, which CI already runs
on every PR and which needs only Vite. They assert what jsdom cannot --
the list is capped and anchored to the input, it stays on screen when it
opens upwards, and a long operation name is not cut off. Reverting the
cap fails the first one with a 501px list.
@romanlutz

Copy link
Copy Markdown
Contributor

The operation you pick applies to attacks you start afterwards; it doesn't relabel existing ones. It also isn't persisted across a page refresh — that's a separate question about where per-user GUI state should live, so I left it out rather than guess.

Yes, would be great if it persisted! Same for operator name. I suppose that one persists in the deployed version due to auth but in the local one it doesn't.

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.

2 participants