The sidebar inbox badge was counting all "mine" issues (issues created
by or assigned to the user) instead of only unread ones. This caused
the badge to show a count (e.g. 14) even when the Unread tab was empty.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add childIssues to the useEffect dependency array so the Properties panel
is refreshed whenever the child issue list updates (e.g. an agent creates
or deletes a sub-task while the panel is open). Previously the panel kept
the snapshot from the initial render of the parent issue.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Address two Greptile review comments:
1. Collapsed parent badge now shows total descendant count at all depths
rather than direct-child count only. Add `countDescendants` utility to
issue-tree.ts (recursive, uses existing childMap) and replace
`children.length` with it in the titleSuffix badge.
2. Add a useEffect that prunes stale IDs from `collapsedParents` whenever
the issues prop changes. Deleted or reassigned issues previously left
orphan IDs in localStorage indefinitely; the effect filters to only IDs
that appear as a parentId in the current issue list and persists the
cleaned array via updateView.
Add four unit tests for countDescendants: leaf node, single-level,
multi-level, and unknown ID.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Extract the inline tree-building logic from IssuesList into a pure
`buildIssueTree` function in lib/issue-tree.ts so it can be unit tested.
Add six tests covering: flat lists, parent-child grouping, multi-level
nesting, orphaned sub-tasks promoted to root, empty input, and list
order preservation.
Add two tests to IssueRow.test.tsx covering the new titleSuffix prop:
renders inline after the title when provided, and renders cleanly when
omitted.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move parent-task link out of the 2-column PropertyRow layout and into
a dedicated full-width section at the bottom of the panel, separated
by a Separator. Sub-tasks are listed in the same section when present.
Each item shows a StatusIcon aligned with the first line of wrapped
title text (items-start + mt-0.5 on the icon wrapper).
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Move collapsedParents from ephemeral useState into IssueViewState,
which is already serialised to localStorage under the scoped key.
Navigating away and back now restores the exact collapsed/expanded
state the user left the list in.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replace the boolean isChild flag with a numeric depth counter.
Each depth level adds 16px left padding via inline style on the
wrapper div, so sub-tasks of sub-tasks (and deeper) are indented
proportionally rather than all aligning at the same level.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
When a parent issue is collapsed, its title is suffixed with
"(N sub-tasks)" so the count remains visible at a glance.
The suffix disappears when the parent is expanded.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
The base IssueRow has sm:pl-1 which overrides pl-6 at sm+ breakpoints.
Adding sm:pl-7 ensures the indent is visible at all screen sizes.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Sub-tasks are now grouped under their parent issue in the list view.
Parent issues with children show a chevron to collapse/expand their subtasks.
Child issues are visually indented to indicate hierarchy.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Keep issue search local to the loaded list, defer heavy result updates, and memoize the rendered list body so typing stays responsive.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Root cause: CSS Grid items default to min-width:auto, allowing content
to push the dialog wider than the viewport on mobile.
- Add [&>*]:min-w-0 on DialogContent to prevent grid children from
expanding beyond the container width
- Keep overflow-x-hidden as safety net
- Remove negative-margin sticky footer that extended beyond bounds
- Revert to standard DialogFooter without negative margins
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Add overflow-x-hidden on DialogContent to prevent horizontal scroll
- Truncate long monospace text (branch names, base refs) in git status grid
- Add min-w-0 on grid cells to allow truncation within CSS grid
- Add overflow-hidden on git status card and repo root section
- Add max-w-full + overflow-x-auto on pre blocks for long commands
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Replaces 12+ inline toggle button implementations across the app with a
shared ToggleSwitch component that scales up on mobile for better touch
targets. Default size is h-6/w-10 on mobile, h-5/w-9 on desktop; "lg"
variant is h-7/w-12 on mobile, h-6/w-11 on desktop.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
Adds a sign out section at the bottom of the general settings page.
Uses authApi.signOut() and invalidates the session query to redirect
to the login page.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
- Reduce padding and text sizes on small screens (p-4/text-xs -> sm:p-6/sm:text-sm)
- Tighter spacing between sections on mobile (space-y-3 -> sm:space-y-4)
- Sticky footer so action buttons stay visible while scrolling
- Grid layout stays 2-col on all sizes for git status
- Add shrink-0 to loading spinner
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies
> - The inline markdown editor (MarkdownEditor / MDXEditor) is used to
edit agent instructions, issue descriptions, and other content
> - When users paste agent instructions copied from terminals or
consoles, extra leading whitespace is uniformly added to every line
> - PR #2572 fixed markdown structure preservation on paste but did not
address the leading whitespace (dedent) problem
> - This pull request adds a Lexical paste normalization plugin that
strips common leading whitespace and normalizes line endings before
MDXEditor processes pasted content
> - The benefit is that pasted content from terminals/consoles renders
correctly without manual cleanup
## What Changed
- **`ui/src/lib/normalize-markdown.ts`** — Pure utility that computes
minimum common indentation across non-empty lines and strips it
(dedent), plus CRLF → LF normalization
- **`ui/src/lib/paste-normalization.ts`** — Lexical `PASTE_COMMAND`
plugin at `CRITICAL` priority that intercepts plain-text pastes,
normalizes the markdown, and re-dispatches cleaned content for MDXEditor
to process. Skips HTML-rich pastes.
- **`ui/src/components/MarkdownEditor.tsx`** — Registers the new plugin;
updates PR #2572's `handlePasteCapture` to use `normalizeMarkdown()`
(dedent + CRLF) instead of `normalizePastedMarkdown()` (CRLF only) for
the markdown-routing path
- **`ui/src/lib/paste-normalization.test.ts`** — 9 unit tests covering
dedent, CRLF normalization, mixed indent, empty lines, single-line
passthrough, and edge cases
## Verification
- `pnpm --dir ui exec vitest run src/lib/paste-normalization.test.ts` —
9 tests pass
- Manual: paste indented agent instructions from a terminal into any
inline markdown editor and confirm leading whitespace is stripped
## Risks
- Low risk. The plugin only activates for plain-text pastes (no HTML
clipboard data). HTML/rich pastes pass through unchanged. Single-line
pastes are not modified. The dedent logic is conservative — it only
strips whitespace common to all non-empty lines.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Replace the client-side-only override store with a real server-side
toggle. When a developer pauses the external override, the server swaps
ALL adapter behavior back to the builtin — execute handler, model listing,
config schema, detection — not just the UI parser.
Server changes:
- registry.ts: builtinFallbacks map + pausedOverrides set + setOverridePaused()
- routes/adapters.ts: PATCH /api/adapters/:type/override endpoint + overridePaused in list
UI changes:
- adapters.ts: setOverridePaused API method + overridePaused on AdapterInfo
- AdapterManager: overrideMutation calls server, instant feedback via invalidate()
- use-disabled-adapters.ts: reads adapter.overridePaused from server response
Removed:
- disabled-overrides-store.ts: no longer needed (server is the source of truth)
Note: already-running agent sessions keep the adapter they started with.
Only new sessions use the swapped adapter.
Builtin adapter types (hermes_local, openclaw_gateway, etc.) could not
be overridden by external adapters on the UI side. The registry always
returned the built-in parser, ignoring the external ui-parser.js shipped
by packages like hermes-paperclip-adapter.
Changes:
- registry.ts: full override lifecycle with generation guard for stale loads
- disabled-overrides-store.ts: client-side override pause state with
useSyncExternalStore reactivity (persisted to localStorage)
- use-disabled-adapters.ts: subscribe to override store changes
- AdapterManager.tsx: separate controls for override pause (client-side)
vs menu visibility (server-side), virtual builtin rows with badges
- adapters.ts: allow reload/reinstall of builtin types when overridden
When an external plugin overrides a built-in adapter type, the
GET /api/adapters response now includes overriddenBuiltin: true. The
Adapter Manager shows an 'Overrides built-in' badge on such adapters.
Batch consecutive system events into a single collapsible group
instead of rendering each as a separate warn-toned block. Shows
count in header, expands on click.
- Sort GET /api/adapters alphabetically by type (reload no longer shuffles)
- Show red Package icon for npm adapters, amber FolderOpen for local path
- Add reinstall confirmation dialog with current vs latest npm version
- Enable Vite polling when running on /mnt/ (WSL inotify doesn't work on NTFS)
- Fix external adapters (hermes, droid) not auto-selected when
navigating with ?adapterType= param — was using a stale
module-level Set built before async adapter registration
- Move SchemaConfigFields to render after thinking effort (same
visual area as Claude's chrome toggle) instead of bottom of
config section
- Extract SelectField into its own component to fix React hooks
order violation when schema fields change between renders
- Add onAdapterChange() subscription in registry.ts so
registerUIAdapter() notifies components when dynamic parsers
load, fixing stale parser for old runs
- Add parserTick to both RunTranscriptView and
useLiveRunTranscripts to force recomputation on parser change
Re-align phase1 with upstream: hermes_local ships via hermes-paperclip-adapter on the server and UI (hermes-local module). Fixes ERR_PNPM_OUTDATED_LOCKFILE from server/package.json missing a dep still present in the lockfile.
Add shared BUILTIN_ADAPTER_TYPES and skip external plugin registration when it would override a built-in type. Docs list Hermes as built-in; Droid remains the primary external example.
Made-with: Cursor
- Plugin loader: install/reload/remove/reinstall external adapters
from npm packages or local directories
- Plugin store persisted at ~/.paperclip/adapter-plugins.json
- Self-healing UI parser resolution with version caching
- UI: Adapter Manager page, dynamic loader, display registry
with humanized names for unknown adapter types
- Dev watch: exclude adapter-plugins dir from tsx watcher
to prevent mid-request server restarts during reinstall
- All consumer fallbacks use getAdapterLabel() for consistent display
- AdapterTypeDropdown uses controlled open state for proper close behavior
- Remove hermes-local from built-in UI (externalized to plugin)
- Add docs for external adapters and UI parser contract