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
Supersedes #2499.
## Thinking Path
1. **Project context**: Paperclip uses a markdown editor
(`MarkdownEditor`) for document editing. Users expect to paste
markdown-formatted text from external sources (like code editors, other
documents) and have it render correctly.
2. **Problem identification**: When users paste plain text containing
markdown syntax (e.g., `# Heading`, `- list item`), the editor was
treating it as plain text, resulting in raw markdown syntax being
displayed rather than formatted content.
3. **Root cause**: The default browser paste behavior doesn't recognize
markdown syntax in plain text. The editor needed to intercept paste
events and detect when the clipboard content looks like markdown.
4. **Solution design**:
- Create a utility (`markdownPaste.ts`) to detect markdown patterns in
plain text
- Add a paste capture handler in `MarkdownEditor` that intercepts paste
events
- When markdown is detected, prevent default paste and use
`insertMarkdown` instead
- Handle edge cases (code blocks, file pastes, HTML content)
## What
- Added `ui/src/lib/markdownPaste.ts`: Utility to detect markdown
patterns and normalize line endings
- Added `ui/src/lib/markdownPaste.test.ts`: Test coverage for markdown
detection
- Modified `ui/src/components/MarkdownEditor.tsx`: Added paste capture
handler to intercept and handle markdown paste
## Why
Users frequently copy markdown content from various sources (GitHub,
documentation, notes) and expect it to render correctly when pasted into
the editor. Without this fix, users see raw markdown syntax (e.g., `#
Title` instead of a formatted heading), which degrades the editing
experience.
## How to Verify
1. Open any document in Paperclip
2. Copy markdown text from an external source (e.g., `# Heading\n\n-
Item 1\n- Item 2`)
3. Paste into the editor
4. **Expected**: The content should render as formatted markdown
(heading + bullet list), not as plain text with markdown syntax
### Test Coverage
```bash
cd ui
npm test -- markdownPaste.test.ts
```
All tests should pass, including:
- Windows line ending normalization (`\r\n` → `\n`)
- Old-Mac line ending normalization (`\r` → `\n`)
- Markdown block detection (headings, lists, code fences, etc.)
- Plain text rejection (non-markdown content)
## Risks
1. **False positives**: Plain text containing markdown-like characters
(e.g., a paragraph starting with `#` as a hashtag) may be incorrectly
treated as markdown. The detection uses a heuristic that requires
block-level markdown patterns, which reduces but doesn't eliminate this
risk.
2. **Removed focus guard**: The previous implementation used
`isFocusedRef` to prevent `onChange` from firing during programmatic
`setMarkdown` calls. This guard was removed as part of refactoring. The
assumption is that MDXEditor does not fire `onChange` during
`setMarkdown`, but this should be monitored for unexpected parent update
loops.
3. **Clipboard compatibility**: The paste handler specifically looks for
`text/plain` content and ignores `text/html` (to preserve existing HTML
paste behavior). This means pasting from rich text editors that provide
both HTML and plain text will continue to use the HTML path, which may
or may not be the desired behavior.
---------
Co-authored-by: 馨冉 <xinxincui239@gmail.com>
The openclaw_gateway adapter hardcodes the Paperclip API key path to
~/.openclaw/workspace/paperclip-claimed-api-key.json in buildWakeText().
In multi-agent OpenClaw deployments, each agent has its own workspace
with its own key file. The hardcoded path forces all agents to share
one key, breaking agent identity isolation.
Add a claimedApiKeyPath field to the adapter config (with UI input)
that allows operators to set a per-agent path. Falls back to the
current default when unset — zero behavior change for existing
deployments.
Fixes#930
On the Paused tab every visible agent is paused, so applying
opacity-50 to all of them is redundant and makes the whole view
dim. Skip the dimming when tab === "paused" in both list and org
chart views. Pass tab prop through to OrgTreeNode for consistency.