Skip to content

Fix verified web audit batch (WEB-D/WEB-E/DRY): 409 banner, diff clamp, outside-click, ChannelModal isPrivate, sidebar lock, emoji filter, shared resize helper - #55

Merged
Aztec03hub merged 1 commit into
mainfrom
worktree-agent-a802d875e8a8c8c9c
Jun 26, 2026
Merged

Fix verified web audit batch (WEB-D/WEB-E/DRY): 409 banner, diff clamp, outside-click, ChannelModal isPrivate, sidebar lock, emoji filter, shared resize helper#55
Aztec03hub merged 1 commit into
mainfrom
worktree-agent-a802d875e8a8c8c9c

Conversation

@Aztec03hub

Copy link
Copy Markdown
Owner

Fixes a batch of verified web-only audit findings (WEB-D, WEB-E, DRY). Web files only; all tests green, build green, Svelte autofixer clean on every edited .svelte/.svelte.js.

WEB-D

  1. 409-conflict banner showed "Someone" / v0. apiPost (lib/api.js) discarded the parsed error response body, so ArtifactPanel's 409 handler read undefined author/version. Now apiPost attaches err.body (parsed JSON, null on non-JSON) on non-2xx; ArtifactPanel already reads body.latest_author / body.latest_version. New test covers the previously-untested 409 path.
  2. Diff-against-oldest requested a non-existent v0. ArtifactPanel set compareVersion = data.version - 1 with no clamp. Now it derives the nearest existing older version (null when none), and the Content/Diff toggle refuses diff when no older version exists.
  3. Version dropdowns never closed on outside click. Added a panel-level mousedown outside-click that closes the primary version dropdown (the open-state ArtifactPanel owns), matching the pattern other menus use. Follow-up: the Compare dropdown's open-state lives in ArtifactDetailHeader.svelte (outside this change's file set) and needs a one-line outside-click addition there.

WEB-E

  1. ChannelModal dropped isPrivate. handleCreate now emits it as a third positional arg, onCreate(name, description, isPrivate) — backward compatible (arity-2 callers ignore it). Follow-up: wire App.sveltestore.createChannel to honor it.
  2. Mute submenu broken for real mouse clicks. The outside-click guard only checked menuEl.contains, but the submenu is portaled to <body>, so a real mousedown closed the menu before the click fired. The guard now also treats the portaled submenu as "inside". New test simulates mousedown then click.
  3. Sidebar Lock glyph keyed off the wrong field. mode === 'private' never matched (mode holds open/invite; privacy lives in visibility). Now visibility === 'private' || mode === 'invite', consistent with ChannelAdminPanel. Exposes data-glyph for testing.
  4. EmojiPicker tabs + search were inert. Added per-category datasets, cross-category search, an empty state, and a dynamic grid label so both controls now filter the grid. New tests.

DRY

  1. ~130 lines of drag-resize duplicated between ArtifactPanel and ThreadPanel. Extracted into web/src/lib/resizable-panel.svelte.js (createResizablePanel), consumed by both panels. Follow-up: mqtt-store.svelte.js keeps its own safeStorage copy (different owner) — left as a noted remaining duplication.

Verification

  • pnpm --dir web test: 1306 passed / 97 files, 0 skipped, 0 failures, no stderr/unhandled (stable across 3 runs).
  • pnpm --dir web build: green.
  • Svelte autofixer: issues: [] on all six edited components + the new module.

🤖 Generated with Claude Code

…u outside-click, ChannelModal isPrivate, sidebar lock, emoji filter; DRY resize helper

WEB-D:
- api.js apiPost now attaches the parsed JSON error body (err.body) on
  non-2xx so ArtifactPanel's 409 banner shows the real latest_author /
  latest_version instead of "Someone" / v0 (+ test for the 409 path).
- ArtifactPanel: derive the diff "compare" version as the nearest EXISTING
  older version (never data.version-1 -> v0), null when none; the Content/Diff
  toggle refuses diff when no older version exists.
- ArtifactPanel: add panel-level outside-click close for the primary version
  dropdown (the header comment claimed this but no listener existed). The
  compare dropdown's open-state lives in ArtifactDetailHeader (out of file
  set) and needs a one-line follow-up there.

WEB-E:
- ChannelModal emits the Private toggle as a 3rd onCreate arg (backward
  compatible; App.svelte -> store.createChannel wiring is a follow-up).
- ChannelContextMenu outside-click guard now treats the portaled Mute
  submenu as "inside" so a real mousedown->click reaches the action
  (+ test simulating mousedown then click).
- SidebarChannelRow lock glyph keyed off visibility==='private' ||
  mode==='invite' (was mode==='private', which never matched), consistent
  with ChannelAdminPanel; exposes data-glyph for testing.
- EmojiPicker category tabs + search now actually filter the grid
  (per-category datasets + cross-category search + empty state) (+ tests).

DRY:
- Extract the ~130-line drag-resize logic duplicated between ArtifactPanel
  and ThreadPanel into lib/resizable-panel.svelte.js (createResizablePanel);
  both panels consume it. mqtt-store's separate safeStorage copy is left as
  a noted follow-up (different owner).
@Aztec03hub
Aztec03hub merged commit 240ba2d into main Jun 26, 2026
7 of 8 checks passed
Aztec03hub added a commit that referenced this pull request Jun 26, 2026
…tion (#55 follow-up) (#56)

The DRY extraction in #55 moved the keyboard nudge step into the shared
createResizablePanel default and dropped the local `const KEY_STEP = 16`,
which broke the source-level pin in e2e/scenarios/10-thread-panel.spec.ts
("ThreadPanel pins KEY_STEP = 16") and turned post-merge CI red on the E2E
job (web unit tests + build + lint stayed green).

Re-declare KEY_STEP=16 alongside its MIN/MAX/DEFAULT/STORAGE_KEY siblings and
pass it explicitly into the helper so it is wired, not dead. No behaviour
change (the helper already defaulted keyStep to 16).
Aztec03hub added a commit that referenced this pull request Jun 26, 2026
…low-up) (#57)

PRIMARY: @mentions inside inline emphasis lost their mention styling.
MessageBubble.parseBody emitted bold/italic/strike tokens whose inner
`value` was raw text never run through the mention/link splitter, so an
@mention in **bold** or *italic* rendered with emphasis styling but no
mention pill/color. Extract the mention+link classification into a shared
`splitTextToSegments` helper, run it over emphasis inner values, and
render the resulting children INSIDE the emphasis wrapper via a recursive
`renderSeg` snippet — mentions/links now inherit emphasis styling AND get
their own chip/link styling. Code spans stay literal (never linkified).
`hasSelfMention` now recurses into emphasis children for the border accent.

SECONDARY (audit follow-ups):
- ChannelModal isPrivate wired end-to-end: App -> store.createChannel(id,
  topic, isPrivate). The create API takes no visibility arg, so createChannel
  sets the local row + retained meta to private AND persists via the admin
  path (setVisibility -> comms_conversation_update), mirroring
  ChannelAdminPanel. #handleMeta now reads visibility from the broadcast.
- ArtifactDetailHeader Compare dropdown closes on outside mousedown via a
  <svelte:window> guard, mirroring ArtifactPanel's primary-dropdown pattern.
- safeStorage DRY: extracted the byte-identical copies from mqtt-store and
  resizable-panel into a shared web/src/lib/safe-storage.js.

Tests: +11 (bold/italic/strike/plain mention chips + code-not-linkified;
createChannel private/public/queued; compare-dropdown outside-click).
1314 -> 1325 tests, all green; build green.
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.

1 participant