Skip to content

[UI] Prompts edit support added and form polished - #2668

Open
toreysoloio wants to merge 6 commits into
kagent-dev:mainfrom
toreysoloio:ui-prompt-edit-support
Open

[UI] Prompts edit support added and form polished#2668
toreysoloio wants to merge 6 commits into
kagent-dev:mainfrom
toreysoloio:ui-prompt-edit-support

Conversation

@toreysoloio

Copy link
Copy Markdown
  • The edit button on prompts table now brings you to the edit form as expected
  • When creating/editing a prompt, clicking the cancel button now properly confirms if you want to throw away changes if any were made
  • Moved the name input above namespace, to be more consistent with other forms
  • Fragment delete button is now red
  • Delete button is now disabled when only 1 fragment with empty content (since otherwise clicking the button doesn't have any effect so UI makes sure there ia always at least 1)
image

delete disabled when only 1 empty fragment:

image

toreysoloio and others added 6 commits September 2, 2026 18:26
…ed it

The pencil on each prompt row went to the read-only detail page — the same
place the library's name beside it already opened. So it promised an edit and
delivered a viewer, while `prompts.update` sat in the client reachable from
nowhere in the app: a library could be created and read and then never
changed.

The API is real, and was checked before any of this was built rather than
assumed from the client: `UpdatePromptTemplate` is declared in the proto,
implemented in `grpcserver`, and writes the ConfigMap in the prompt template
service. It replaces the whole `data` map rather than merging into it, so the
form is seeded from a full read and says plainly that a fragment removed
there is deleted — "remove this row" and "delete this fragment on save" are
one act and only one of them was visible. The name and namespace address the
ConfigMap, so they are shown locked; an edit cannot rename a library or move
it.

`PromptForm` is now the fields for both surfaces, on the division of labour
the model form uses: the form owns the fields and the rules, each page owns
its request and what follows. The create page is what remains of it after the
extraction, and `PromptEditPage` is the other caller, at
`/prompts/:namespace/:name/edit`. Sharing them is the point — two renderings
of one thing drift, and the one nobody edits is the one that quietly stops
offering something the API gained. The rules live in `promptDraft` beside
them, where they can be read and tested without a component around them.

A save sweeps every read keyed as a prompt library rather than refreshing the
one it came from. The list's key carries the namespace filter, so refreshing
`usePrompts()` would have refreshed the unfiltered read and left whichever
filtered list is on screen showing the old key count.

Two things the fixtures had wrong, both found by driving this: the mock's
summary returned fragment keys in insertion order where the service sorts
them, so a library edited through the app came back ordered differently from
the same library read from a cluster. The detail page now sorts its fragments
too, for the same reason the form does.

The spec covers both ways in, the discard prompt, the save landing on the
library, the list behind it being re-read, and the two refusals the
controller would also make. It also covers a case worth having: a deep link
to the edit address of a library that is gone says so instead of taking input
for a save that cannot land. The create path is covered for the first time,
since it went through the same extraction.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Torey Scheer <torey.scheer@solo.io>
The discard question only ever appeared on the form's own Cancel button. The
"Back to library" link beside it, the sidebar, and the browser's back button
all threw a draft away without a word — and the header link is the more
obvious way to leave, so the guard was invisible in practice. A confirmation
on one exit is worse than none: it teaches a reader the work is held safe and
then loses it somewhere else.

So the draft blocks *leaving* rather than one button. `useBlocker` catches
every in-app navigation, Cancel included — which is why Cancel is now an
ordinary link or callback with no question of its own, and why Discard
resumes the navigation that was blocked instead of going somewhere this form
picked. A `beforeunload` listener covers the exits the router cannot see: a
reload, a closed tab, a typed address.

Two consequences worth stating. A save navigates as part of succeeding, so
the draft stops counting as unsaved before the caller navigates, or the guard
would stop the save's own navigation and ask whether to discard it; a failed
save marks it unsaved again, because nothing was written and the work is
still on screen. And the baseline is captured at mount rather than read from
the `initial` prop, so a library being written for the first time is guarded
too — its baseline is the empty draft, and a typed name is something to lose.

An untouched form still leaves in silence. A question over a form nobody has
touched is the thing that teaches people to click through the next one.

Every exit is pinned in the spec: the header link and the sidebar ask and
keep the draft, Discard leaves for where the reader was heading, the create
form asks once something is typed, and the save's own navigation is not
questioned — the URL assertion there is what fails if it ever is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Torey Scheer <torey.scheer@solo.io>
Every other authoring form in the app asks for the name first — the MCP
server form, the model form, the agent template form — and the prompt form
was the one that did not, with nothing written down claiming a reason. The
namespace also arrives filled in with a default and is usually left alone,
so leading with it put a box to tab past in front of the empty field the
reader actually came to fill.

The spec gains two things. The fragment remove button is asserted to carry
antd's danger class: a default button hovers towards the brand colour, which
turned the one destructive control in the row purple under the pointer while
every delete elsewhere went red.

And the create journey no longer races itself. Filling the name immediately
after dismissing the discard dialog let the state change that closes the
dialog re-render the field back to its previous value, so Firefox created a
library under the name the earlier step had typed. Waiting for the dialog to
go and asserting the value took is the fix; nothing a reader can hit at
human speed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Torey Scheer <torey.scheer@solo.io>

@cjlg-soloio cjlg-soloio left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

didn't test but code and screenshots lgtm!

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