feat(skills): edit text assets in place - #15
Merged
Conversation
Changing one character in a script meant downloading the file, editing it, and
re-uploading — which is how a wrong BASE_URL sat in a published skill for a
while. The preview dialog now has an edit mode.
No backend change. `PUT /skills/{id}/assets/{path}` was already an upsert of the
whole file, so "edit" is a different entry point rather than a different
operation. The whole body still goes over the wire on save; that mismatch
between what the user does and what is transmitted is fine and does not justify
a second, incremental endpoint.
## Two hard limits, both about silently corrupting a file
Extracted into `lib/assetEditGuard` so they are testable rather than buried in
JSX:
- **Truncated content cannot be saved.** The preview keeps only the first 200k
characters. Saving that would erase the rest of the file, and the PUT would
succeed, and the UI would say "saved" — nothing anywhere reports a problem.
The editor is withheld in that case, with a line saying why.
- **Images and Office files cannot be edited as text.** The bytes are already
ruined by the time they have been read as a string.
Mutation-verified three ways: dropping the truncation guard, the binary guard,
or the permission check each turns the suite red.
`kind` is echoed back exactly as the server sent it, typed as `string` rather
than narrowed to `SkillAssetKind`. PUT is an upsert, so a wrong `kind` silently
reclassifies a script as a reference — it stops being executed, and nothing
errors. Narrowing would force a future backend kind to be mapped onto some known
value, which is precisely that failure.
Editing shows that saving writes to the draft, and that a published skill drops
back to draft and needs review again. Without it the natural assumption is that
the change is live.
## The dialog collapsed, and the first two fixes were wrong
`DialogContent`'s base classes contain `grid`. `twMerge` does not treat `grid`
and `flex` as conflicting — they belong to the `display` and `flex-direction`
groups — so both survive into the DOM, and in the compiled CSS `.grid` is
defined after `.flex` at equal specificity. `display: grid` wins, and the entire
flex chain (`flex-1`, `min-h-0`, `flex-col`) was dead code.
That is why two rounds of adjusting flex properties changed nothing. The first
attempt blamed `flex-basis: 0` collapsing inside an auto-height container. The
second found the real cause but reached for `!flex`, which turned out never to
compile.
Layout is now expressed with inline styles: they beat any class, so there is no
arguing with class order or with whether an important-prefixed utility got
generated.
258 passed. `tsc` unchanged at 112 non-TS5097 errors.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Changing one character in a script meant downloading the file, editing it and
re-uploading. That is how a
BASE_URLwith a missing character sat inside apublished skill for a while. The preview dialog now has an edit mode.
No backend change.
PUT /skills/{id}/assets/{path}was already an upsert ofthe whole file, so "edit" is a different entry point rather than a different
operation. The full body still goes over the wire on save; that gap between what
the user does and what is transmitted is fine and does not justify a second,
incremental endpoint.
Two hard limits, both about silently corrupting a file
Extracted into
lib/assetEditGuardso they are testable rather than buried inJSX — they are data-safety decisions, not presentation details.
characters. Saving that erases the rest of the file — and the PUT succeeds, and
the UI says "saved", and nothing anywhere reports a problem. You would find out
the next time you opened the file. The editor is withheld, with a line saying
why.
ruined by the time they have been read as a string.
Mutation-verified three ways: removing the truncation guard, the binary guard, or
the permission check each turns the suite red.
kindis echoed back verbatimTyped as
stringrather than narrowed toSkillAssetKind. PUT is an upsert, soa wrong
kindsilently reclassifies a script as a reference — it stops beingexecuted and nothing errors. Narrowing would force a future backend kind to be
mapped onto some known value, which is exactly that failure.
Edit mode also states that saving writes to the draft and that a published skill
drops back to draft pending review. Without that line the natural assumption is
that the change is already live.
The dialog collapsed, and two fixes for it were wrong
DialogContent's base classes containgrid.twMergedoes not treatgridand
flexas conflicting — they belong to thedisplayandflex-directiongroups — so both survive into the DOM. In the compiled CSS,
.gridis definedafter
.flexat equal specificity, sodisplay: gridwins and the entire flexchain (
flex-1,min-h-0,flex-col) was dead code.That is why two rounds of tuning flex properties changed nothing on screen. The
first attempt blamed
flex-basis: 0collapsing inside an auto-height container —wrong layer entirely. The second found the real cause but reached for
!flex,which turned out never to be generated.
Layout is now inline styles. They beat any class, so there is no arguing with
class order, and no dependency on whether an important-prefixed utility compiles.
Not verified
The rendered dialog was not measured. The asset panel needs a logged-in session,
which is not automatable from here. The layout diagnosis is evidence-based — the
positions of
.gridand.flexwere read out of the compiled CSS — but thefinal appearance was confirmed by the repo owner in a browser, not by me.
258 passed.
tscunchanged at 112 non-TS5097 errors.🤖 Generated with Claude Code