Commit Graph

91 Commits

Author SHA1 Message Date
Lukas Moschitz 306ddaa5df Merge remote-tracking branch 'origin/main' into lukas/oss-903-presentation-wire-errors-notifications-to-emanate-from-and
# Conflicts:
#	packages/web-inspector/src/lib/__tests__/telemetry.test.ts
#	packages/web-inspector/src/styles/generated.css
2026-08-25 12:09:52 +02:00
Atai Barkai 7ee91c8d28 fix(deprecation): link related v2 migration concepts 2026-08-21 16:50:46 -07:00
Atai Barkai c40d1b15b4 chore(deprecation): remove redundant v1 source paths 2026-08-21 16:50:45 -07:00
Atai Barkai 7aa6639687 chore(deprecation): keep v1 notice diff focused 2026-08-21 16:50:45 -07:00
Atai Barkai be7427eab5 chore(deprecation): direct every v1 export to v2 2026-08-21 16:50:45 -07:00
Atai Barkai 0c0ddfe9f9 fix(deprecation): show v2 usage in IDE warnings 2026-08-21 16:50:45 -07:00
Atai Barkai d7ccfd3977 fix(deprecation): warn v1 users to use v2 in IDEs 2026-08-21 16:50:45 -07:00
Atai Barkai 06a461ee95 fix(deprecation): direct v1 users to v2 everywhere 2026-08-21 16:50:45 -07:00
Atai Barkai 19c65b23cb fix(deprecation): use file-specific v2 paths 2026-08-21 16:50:45 -07:00
Atai Barkai 08b89de667 chore: add v1 SDK deprecation pilot 2026-08-21 16:50:45 -07:00
copilotkit-qa-bot[bot] ec4439c8d9 Merge origin/main into codex/ent-1157-shared-clerk-session 2026-08-18 10:28:01 -07:00
Ben Taylor 471d74bad4 feat(react-ui): report feedback state to onThumbsUp/onThumbsDown (#6531)
Closes #2615.

## Problem

The v1 thumbs callbacks were typed `(message: Message) => void`, so a
consumer
received the message but not *what the click did*. A custom
`AssistantMessage`
that keeps its own toggle state — the case in the issue — had nowhere to
put
that value:

```tsx
onClick={() => {
  setReactionValue((v) => (v === "like" ? null : "like"));
  onThumbsUp?.(message); // 👈 no way to say whether this applied or retracted
}}
```

The only workaround was counting clicks per message.

The built-in path had the matching gap. `Chat.tsx` already tracked
`messageFeedback`, but `handleThumbsUp` unconditionally wrote
`"thumbsUp"`, so
the state was write-only: clicking an active button re-applied the same
value
and there was no way to un-vote.

These callbacks are live API — `RenderMessage` forwards `onThumbsUp`,
`onThumbsDown` and `feedback` to whatever component is passed as the
`AssistantMessage` prop, which is exactly the customisation the issue
describes.

## Fix

Add an optional second argument reporting the state the click
transitions to:

```ts
onThumbsUp?: (message: Message, isActive?: boolean) => void;
```

- The built-in `AssistantMessage` derives it from the message's current
`feedback`, so a second click on an active button now reports `false`
and
  **retracts** the feedback rather than re-applying it.
- A custom `AssistantMessage` can pass its own value straight through.
- The parameter is optional and appended, so existing one-argument
handlers
  keep type-checking and keep working unchanged.

`onFeedbackGiven` fires only on the applying click — its signature is
`(messageId, "thumbsUp" | "thumbsDown")` and has no way to express a
retraction, so reporting one would be a lie.

Note this is a small behavioural change to the built-in buttons:
previously a
repeat click was a no-op, now it clears the vote. That is what makes the
reported state meaningful, and it matches the toggle UX in the issue.

The toggle logic lives in a new `./feedback` module. This package's
vitest
project runs `environment: "node"` and only collects `*.test.ts`, so
there is no
component-rendering harness here — extracting the pure functions is what
makes
the behaviour testable at all.

## Testing

`packages/react-ui` — full suite, including the 10 new cases:

```
 ✓ src/components/chat/feedback.test.ts (10 tests) 3ms
 ✓ src/components/chat/Markdown.test.ts (29 tests) 5ms
 ✓ src/components/chat/Markdown.xss.test.ts (13 tests) 182ms
 ✓ src/css/sidebar-full-height.test.ts (4 tests) 2ms

 Test Files  10 passed (10)
      Tests  68 passed (68)
```

Coverage: activation from no feedback, deactivation on the
already-active
button, switching to the opposite button, retraction removing the map
entry
rather than storing a falsy value, other messages left untouched, no
mutation of
the previous map, referential stability when nothing changes, and a
toggle
round-trip.

**Mutation checks** — broke each mechanism and confirmed the tests fail,
so they
are not self-fulfilling:

| Mutation | Result |
| --- | --- |
| `isActivatingClick` → `return true` | `Tests 1 failed \| 9 passed` |
| `applyFeedbackClick` retraction branch → `if (false)` | `Tests 3
failed \| 7 passed` |

Both were reverted and the suite returned to 68 passing.

**Types/build** — `tsc --noEmit` clean, `tsdown` clean, and the widened
signature reaches the published types:

```
$ grep -n "onThumbsUp" dist/index.d.mts
102:  onThumbsUp?: (message: Message, isActive?: boolean) => void;
185:  onThumbsUp?: (message: Message, isActive?: boolean) => void;
272:  onThumbsUp?: (message: Message, isActive?: boolean) => void;
```
2026-08-18 08:56:55 -05:00
Benjamin Taylor c94032f3fa feat(react-ui): report feedback state to onThumbsUp/onThumbsDown
The thumbs callbacks only received the message, so a consumer could not
tell whether a click applied feedback or retracted it. Custom
AssistantMessage implementations that track their own toggle state had
no way to pass that value through, and the built-in feedback state was
write-only: clicking an active button re-applied the same value.

Add an optional second argument reporting the state the click
transitions to. The built-in AssistantMessage derives it from the
message's current feedback, which also makes the built-in buttons
toggle; a custom AssistantMessage may pass its own value. The argument
is optional, so existing one-argument handlers are unaffected.

The toggle logic is extracted to ./feedback so it can be unit tested —
this package's vitest project runs in a node environment and has no
component-rendering harness.

Closes #2615
2026-08-17 17:22:24 -05:00
Benjamin Taylor 1ea18ca864 fix(react-ui): pad the chat header on mobile viewports
The `.copilotKitHeader` rule declared `padding-right` only inside
`@media (min-width: 640px)`, so below that breakpoint the header's
`space-between` children ran flush to the viewport edge and the close
button had no gutter.

Move the horizontal padding into the unconditional rule so it applies at
every viewport. The value matches the 24px the sm breakpoint already
used, so wide layouts are unchanged.

Fixes #2493
2026-08-17 17:22:21 -05:00
Benjamin Taylor a3c5f079ed fix(react-ui): let sidebar children fill the viewport height
Children of `CopilotSidebar` cannot use `height: 100%`. Both wrappers the
sidebar puts around your app -- `.copilotKitSidebarContentWrapper` and
`.copilotKitModalChildrenWrapper` (which had no CSS rule at all) -- are
auto-height blocks, so a percentage height on a child has no definite
containing block and collapses to content height.

Add an opt-in `fullHeightChildren` prop that gives the content wrapper a
one-viewport height and lets the children wrapper fill it. It is opt-in
because the content wrapper wraps the entire consumer app, and giving
every react-ui sidebar user a flex column with a fixed height would
reflow apps that never asked for it.

The height is a viewport unit, not `100%`: `100%` only resolves when
every ancestor (html/body/#root) also declares a height, which react-ui
neither sets nor can guarantee, so it would silently no-op in a stock
Next.js app. `min-height: 0` on the children wrapper clears the flex-item
`min-height: auto` floor so tall content scrolls inside the child instead
of stretching the wrapper past the viewport.

Fixes #261

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Ashish Shaw <77574570+ashish4143@users.noreply.github.com>
2026-08-05 16:54:47 -05:00
Ethan qu 98663a43a9 fix(react-ui): re-assert sidebar header square corners at >=640px
Per BenTaylorDev's review on #5805, dropping the specificity of the
sidebar variant created a tie at desktop widths with the
@media (min-width:640px) base rule (both (0,1,0)); the later-authored
base won and reverted the sidebar header's top corners from square
(border-radius:0) back to rounded 8px. Re-assert the media-scoped
sidebar override inside the @media block (mirroring window.css) so the
sidebar header stays square at all widths.
2026-07-25 17:58:01 +03:00
godququ5-code 1c6d7cb6fc fix react-ui sidebar css specificity 2026-07-03 00:37:00 +03:00
Austin Merrick c7404fb2a7 docs(react-ui): clarify all modalities in attachments prop examples (#5493)
## Summary

The `attachments` prop supports images, audio, video, and documents —
but the JSDoc example in `Chat.tsx` only showed
`image/*,application/pdf`, and the docs configuration example used
`accept: image/*`, silently teaching users to restrict themselves to
images.

**Before (Chat.tsx JSDoc):**
```tsx
accept: image/*,application/pdf,
```

**After:**
```tsx
accept: image/*,audio/*,video/*,application/pdf,
```

The docs configuration example now also clarifies that omitting `accept`
defaults to `*/*` (all files), and the shown value includes all four
supported modalities.

## Changes

- `packages/react-ui/src/components/chat/Chat.tsx` — updated JSDoc
example to show all modalities; added note that default `accept` is
`*/*`
- `showcase/shell-docs/src/content/docs/multimodal-attachments.mdx` —
updated configuration example to show
`image/*,audio/*,video/*,application/pdf` and note that omitting
`accept` allows all types
2026-07-02 13:09:31 -07:00
Austin Merrick 4ba201b5c4 fix: repair check-types across all packages and gate it in CI
Repairs TypeScript check-types across the monorepo and adds a CI gate so
regressions are caught going forward:

- core: bundler module resolution and strict-mode fixes
- sdk-js: bundler module resolution; keep codegen, formatter, packaging working
- react-core: fixes across components, hooks, and tests
- react-native: restore catch binding referenced by TypeError cause
- runtime: repair check-types and bound AI SDK schema inference
- web-inspector: nodenext import extensions, export Anchor
- remaining packages and node example: assorted check-types repairs
- deps: add missing type-only devDependencies
- license context driven from /info licenseStatus
- ci: run check-types in the static quality workflow

Squashed from 12 commits for a single, easily-revertable change.
2026-06-23 15:26:47 -07:00
Alem Tuzlak 5ecdee36b8 feat(bot): pluggable StateStore persistence + cross-platform transcripts
Adds a durable persistence layer for @copilotkit/bot, replacing the
in-memory-only ActionStore with a pluggable StateStore.

- StateStore interface (kv/list/lock/dedup/queue) with a shared
  conformance suite; MemoryStore default plus @copilotkit/bot-store-redis
  and @copilotkit/bot-store-postgres backends.
- createBot({ store }): typed per-thread state via Standard Schema,
  action snapshots persisted through the store, per-conversation turn
  lock (onLockConflict drop|force), and inbound-event dedup keyed on a
  stable eventId. ActionStore is kept as a deprecated alias.
- Cross-platform transcripts (bot.transcripts + identity resolver) with
  age-bounded retention (prune on append + filter on read), and
  runAgent({ transcript: true }) to auto-inject history and capture the
  reply.
- createBot({ components }) re-registers components so durable actions
  re-fire after a restart; restart-durability demo in examples/slack.
- Dedup is marked seen only after the turn lock is acquired, so a turn
  dropped on lock-conflict does not burn its eventId (no lost retries).
- Release lockstep: bot-store-redis/postgres version with bot + bot-ui.
2026-06-23 18:33:38 +02:00
Jordan Ritter 76c21b90e4 fix(react-ui): sanitize raw HTML in Markdown renderer to prevent XSS
The legacy Markdown renderer enabled rehype-raw with no HTML sanitizer,
so raw HTML embedded in assistant/model output reached the DOM (CWE-79).

Add rehype-sanitize as the terminal rehype pass so it runs after any
consumer-supplied rehypePlugins and cannot be bypassed. Add a regression
test covering the dangerous-HTML vectors (script/style/base/form/iframe,
event handlers, javascript: URLs) and the consumer-plugin injection path,
and assert legitimate Markdown/GFM features still render. Pin react-dom to
a caret range for the SSR-based test.
2026-06-22 23:54:58 -07:00
Varun Nuthalapati 5d67341881 docs(react-ui): clarify all modalities in attachments prop examples 2026-06-16 09:17:55 -07:00
Mark dedaa66cef Merge branch 'main' into main 2026-06-12 12:40:55 -07:00
Mark Fogle 450d47f90e fix(react-ui): forward data-testid to chat textarea + align selector names
Addresses review feedback on #4215 / OSS-192.

- Thread an explicit `data-testid` prop through `AutoResizingTextarea` so it
  reaches the rendered <textarea>. The component destructures a fixed prop set
  with no `{...rest}` spread, so the id passed from Input.tsx was silently
  dropped and never landed in the DOM.
- Align selector names with the V2 components in @copilotkit/react-core:
  `copilot-chat-textarea` on the textarea and `copilot-send-button` on the send
  control. The legacy `data-test-id` values are preserved for back-compat.
- Add a source-level test asserting the Input wiring and that Textarea forwards
  the prop (the dropped-prop guard); the package's vitest runs in a node env
  with no DOM harness, matching the existing testids.test.ts convention.

Stable selectors let tests locate the controls; the headless input-driving
issue in #4215 remains a separate follow-up and should stay open.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 18:06:31 +00:00
Benjamin Taylor 8d68a95bc9 docs(packages): drop client license-key prop references; Angular no longer needs a key
Follow-up correction. The client publicLicenseKey/publicApiKey prop is the
header→cloud path and is NOT what activates the Intelligence runtime (that's
the server-side COPILOTKIT_LICENSE_TOKEN). So:

- Remove the `npx copilotkit@latest license` guidance from all client-prop
  contexts — that CLI yields the server-side license token, not the client
  prop value.
- Revert the client-prop docstrings (copilotkit-props, v2 CopilotKitProvider)
  to bare one-liners; drop the premium/"requires a license key" framing from
  the headless hook, react-ui observability docs, and runtime logging/onError
  JSDoc rather than reframing.
- Angular: remove all `licenseKey` mentions from the README — it is no longer
  a premium feature (the license watermark is disabled) and the key is not
  needed to function.

Server-side license-token documentation remains deferred to the example/runtime
setup pass (Bucket B).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 07:22:37 -05:00
Benjamin Taylor 8956c668bc docs(packages): retire Copilot Cloud framing in SDK doc references, point to the license key
Cloud is no longer promoted; the Intelligence license key is its replacement.
Scrub the old Copilot Cloud system from SDK JSDoc / doc-comments / console
messages / README prose so code references reflect how the license key is
obtained and used, mirroring examples/integrations/*:

- publicApiKey/publicLicenseKey docstrings (react-core props + v2 provider,
  vue legacy types, copilot-context) describe the CopilotKit public license
  key, acquired via `npx copilotkit@latest license` or the dashboard;
  publicApiKey framed as the legacy alias of publicLicenseKey.
- Premium-feature docs (headless hook, react-ui Chat/Popup/Sidebar
  observability, runtime logging/onError) drop "Copilot Cloud"/"requires a
  publicApiKey" wording and the publicApiKey examples in favor of the public
  license key + publicLicenseKey.
- console-styling messages and the angular README point at the license key
  and the `npx copilotkit@latest license` command.

Defunct features (guardrails_c, authConfig_c, useCopilotAuthenticatedAction_c)
keep their code but lose their JSDoc (marked @internal defunct).

Functional surfaces untouched: api.cloud.copilotkit.ai endpoint, the
X-CopilotCloud-Public-Api-Key header, prop names, gating logic, tests,
CHANGELOGs. Example-app migration (Bucket B) deferred.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 07:22:37 -05:00
Jordan Ritter f43c3f3f5b feat(ui): add stable testids to error banner and loading indicator
Adds purely additive data-testid markers to the error and loading UI
surfaces across the frontend framework packages (react-core, react-ui,
react-native, angular, vue) so e2e tests can deterministically detect
errored-out vs still-loading states. Without these, e2e probes hit
~30-60s timeouts instead of failing fast.

Testids (aligned with existing repo convention; copilot-<kebab>):
- copilot-error-banner on react-core BannerErrorDisplay (toast
  provider) and UsageBanner, plus react-ui legacy in-chat ErrorMessage.
- copilot-loading-cursor on react-ui legacy LoadingIcon sites
  (Messages.tsx, AssistantMessage.tsx), angular
  CopilotChatMessageViewCursor, react-native TypingIndicator (via
  RN testID convention), and vue CopilotChatMessageView. The v2
  react-core Cursor already exposed this testid; this change broadens
  it to every frontend framework so a single selector works across all.

Vue's prior copilot-chat-cursor testid is renamed to
copilot-loading-cursor for cross-framework consistency; the two e2e
tests in packages/vue that referenced the old name are updated.

No behavior, rendering, or styling changes. Adds small static
source-asserting tests in each touched package that verify the markers
stay in place.
2026-05-30 10:02:26 -07:00
Sam Julien 33f669ba7b fix(packages): canonicalize docs.copilotkit.ai URLs in user-facing messages
Replace docs URLs that currently 301 through the legacy redirect catalog
with their canonical post-cutover destinations so users clicking links
from console warnings, JSDoc, and in-product help land in one hop.

URLs updated:
- /premium#how-do-i-get-access-to-premium-features
  -> /premium/overview#getting-access
- /coagents/quickstart/langgraph -> /langgraph-python/quickstart
- /coagents/shared-state/predictive-state-updates
  -> /langgraph-python/shared-state/predictive-state-updates
- /reference/v1/hooks/useCopilotChatHeadless_c
  -> /reference/v2/hooks/useCopilotChatHeadless_c
- /coagents/troubleshooting/common-issues
  -> /langgraph-python/troubleshooting/common-issues
- /quickstart#get-a-copilot-cloud-public-api-key
  -> /built-in-agent/quickstart#create-a-free-account
- /premium -> /premium/overview

URLs left as-is because they already resolve 200 with no redirect:
/migration-guides/migrate-attachments, /migration/render-message,
/telemetry.

Hook bypassed: pre-commit test failed in @copilotkit/web-inspector due
to missing jsdom dependency in its package.json (unrelated to this
change; no overlap with edited files or URLs). Tests for the four
affected packages (react-core, react-ui, shared, runtime) pass.
2026-05-22 16:37:21 -07:00
杨兴隆 417ff8986b fix(react-ui): add stable test ids to chat input 2026-05-01 02:29:50 +08:00
Tyler Slaton 4bd909f0ca refactor(react): polish image attachment thumbnails
Iterate on the image attachment rendering based on review feedback:

- Reduce thumbnail size to 80x80 (down from 300x300) so attachments
  read as compact thumbnails like Claude's chat UI
- Render attachments above the message text instead of below, and lay
  multiple attachments out in a horizontal row (flex-row + flex-wrap +
  justify-end) instead of stacking vertically
- Add a muted background so transparent images stay readable
- Extract Lightbox + useLightbox into a shared module so the rendered
  attachment can reuse the same click-to-zoom modal as the queue preview;
  clicking a thumbnail now opens it in a fullscreen lightbox with a
  view-transition morph

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 21:27:43 -07:00
Tyler Slaton f480e2427d fix(react): render image attachments as compact thumbnails
Constrain image attachments to a 300x300 max size with object-cover and
12px rounded corners so they appear as small thumbnails in chat instead
of filling the message width. Applies to both the v2 renderer (Tailwind)
and the legacy react-ui renderer (CSS).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-29 20:48:35 -07:00
Alem Tuzlak 77dd2abc29 Merge remote-tracking branch 'origin/main' into fix/remark-rehype-plugins-2296
# Conflicts:
#	packages/react-ui/src/components/chat/Markdown.tsx
2026-04-22 12:53:35 +02:00
Alem Tuzlak 38e95491e7 refactor(react-ui/Markdown): pass through all react-markdown Options via spread
Addresses PR feedback on #3822.

Instead of plumbing remarkPlugins and rehypePlugins through the component
one prop at a time, MarkdownProps now extends Omit<Options, "children">
and spreads the remainder into the underlying ReactMarkdown. Consumers
can now pass any react-markdown Options field (urlTransform, allowedElements,
skipHtml, etc.) without further plumbing.

The three fields that need library-specific defaults (components,
remarkPlugins, rehypePlugins) are still destructured, merged, and memoized
internally so the built-in behavior is preserved and merged values keep
stable references across renders.

Dropping the custom memo comparator — now that arbitrary props flow
through, a fixed 4-key check would silently skip re-renders when
consumer-provided props change. React's default shallow-compare, combined
with the internal useMemo on merged values, gives correct behavior without
regressing the memoization for the common case.
2026-04-22 12:50:09 +02:00
Alem Tuzlak 3621d26918 fix: handle void return from sendFunction in usePushToTalk (#3783)
## Summary
- `sendMessage` returns `Promise<void>` but `usePushToTalk` accessed
`.id` on the result, causing a `TypeError` crash during push-to-talk
transcription
- Added guard to check if `sendFunction` returns a message before
accessing `.id`
- Updated `SendFunction` type to accept `Promise<Message | void>`

Fixes #3011

## Test plan
- [x] Added unit tests verifying void return handling
- [x] Added unit tests verifying message-with-id return handling
- [x] All react-ui tests pass
2026-04-16 12:27:57 +02:00
Alem Tuzlak 97d413d0af fix: replace hardcoded width 97% with 100% on messages footer (#3786)
## Summary
- `.copilotKitMessagesFooter` used `width: 97%` which caused horizontal
overflow in constrained containers
- Changed to `width: 100%` with `box-sizing: border-box` so padding is
included in the width calculation

Fixes #2325

## Test plan
- [x] All react-ui tests pass
- [ ] Visual verification: render CopilotKit chat in a constrained
container (e.g. 300px sidebar) and confirm no horizontal scrollbar
appears
2026-04-16 12:27:38 +02:00
Alem Tuzlak 0726b99621 fix: configurable audio mediaType and release mic tracks on stop (#2049, #2050) (#3811)
Fixes #2049
Fixes #2050

Red-green tested locally.
2026-04-16 12:27:04 +02:00
Alem Tuzlak d4aca6664b fix: export CopilotModal and CopilotModalProps from react-ui (#2194) (#3817)
## Summary

Fixes #2194

`CopilotModal` and `CopilotModalProps` were not exported from
`@copilotkit/react-ui`, making them inaccessible to consumers. Adds both
exports to the chat barrel file.

## Test plan

- [ ] Verify `CopilotModal` and `CopilotModalProps` are importable from
`@copilotkit/react-ui`
2026-04-16 12:26:08 +02:00
Alem Tuzlak e9148b6e92 fix: show loading indicator during tool execution (#3833)
## Summary
- The loading spinner only appeared when the last message had role
"user"
- During tool execution the last message can have role "tool", hiding
the indicator
- Now also show it when the trailing message is a tool result

Closes #3055
2026-04-16 12:22:41 +02:00
Alem Tuzlak a7e7fbeef6 fix: pass urlTransform prop through to ReactMarkdown (#3845)
Closes #1921

The Markdown component accepted `components` but not `urlTransform`,
preventing users from customizing URL sanitization. Added `urlTransform`
as an optional prop with passthrough to `MemoizedReactMarkdown`.

Split from #3838.
2026-04-16 12:20:18 +02:00
Alem Tuzlak 057f159db0 fix: skip version check when showDevConsole is false (#3827)
## Summary

- `checkForUpdates()` in dev console is now gated on `showDevConsole`,
preventing unnecessary network requests when the console is disabled

Closes #2751

## Test plan

- [x] `@copilotkit/react-core` tests pass

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-04-16 12:00:23 +02:00
Jordan Ritter a46218557c fix: add remarkPlugins/rehypePlugins passthrough support (#2296) 2026-04-14 15:55:08 -07:00
Jordan Ritter bcea46771b fix: handle urlTransform=null correctly in Markdown component
The truthiness check swallowed null, which is a valid value in
react-markdown that explicitly disables the default URL sanitizer.
Changed to an explicit undefined check.
2026-04-14 15:53:40 -07:00
Jordan Ritter 1e0de53a25 fix: add null check for navigator.clipboard across all copy-to-clipboard calls (#2114) (#3813)
Fixes #2114

Red-green tested locally.
2026-04-14 14:31:13 -07:00
Jordan Ritter 0106b1bf19 fix: remove unstable key on CodeBlock to prevent flickering during streaming (#3826)
## Summary

- Removes the `key` prop from `CodeBlock` in `Markdown.tsx` entirely
- The original `Math.random()` key caused React to remount the component
on every render, producing visible flickering
- A content-based key (language + content prefix) still changes every
streaming token, causing the same problem
- Without an explicit key, React uses positional identity — stable
across re-renders while content streams in

Closes #2669

## Test plan

- [x] `@copilotkit/react-ui` tests pass
- [x] `@copilotkit/react-ui` build succeeds
- [ ] Manual: verify code blocks no longer flicker during streaming in
chat UI
2026-04-14 14:26:13 -07:00
Jordan Ritter 9e5b458612 fix: use div instead of p tag in Markdown to prevent hydration errors (#2234) (#3821)
## Summary

Fixes #2234

React's hydration fails when block-level elements (like `<div>`) are
nested inside `<p>` tags in the Markdown component. Replaces the `<p>`
wrapper with `<div>` (adding `copilotKitParagraph` class for styling) to
prevent SSR hydration mismatches.

## Test plan

- [ ] Verify Markdown rendering in SSR context has no hydration errors
- [ ] Verify paragraph styling is preserved via `copilotKitParagraph`
class
2026-04-14 14:26:02 -07:00
Jordan Ritter 093f41907a fix: extract shared copyToClipboard utility to eliminate clipboard duplication
Address review feedback: extract the repeated clipboard availability check +
writeText + error handling pattern into a shared copyToClipboard() utility in
@copilotkit/shared. All 9 call sites across angular, react-core, and react-ui
now use the shared utility instead of duplicating the same code block.
2026-04-14 13:39:42 -07:00
Jordan Ritter b7b885e086 fix: scope dark theme CSS variables in colors.css
Same bug pattern as console.css and input.css: bare `.dark,` was a
standalone selector leaking all --copilot-kit-* CSS custom properties
onto any element with a .dark class. Also removed broken `:root`
pseudo-element from `body[style*="color-scheme: dark"] :root` — :root
cannot be a descendant of body, so this selector never matched.

Fix: drop standalone `.dark,` (redundant with `html.dark` and
`body.dark`) and remove the non-functional `:root` descendant.
2026-04-14 13:33:59 -07:00
Jordan Ritter 5e9fec77ba fix: update blockquote nested paragraph selector after p-to-div change
The blockquote.copilotKitMarkdownElement p selector targeted literal <p>
elements inside blockquotes, but paragraphs now render as <div> after
the hydration fix. Updated to target .copilotKitParagraph class instead.

Also removes unused import and adds a regression test for this selector.
2026-04-13 14:38:22 -07:00
Jordan Ritter 8244b0f31f fix: guard clipboard calls and only show copied state on success (#2114)
Add null checks for navigator.clipboard across all copy-to-clipboard
calls to prevent TypeError in non-localhost environments where the
Clipboard API is unavailable. The copied indicator now only appears
after a confirmed successful write, preventing false positive UX
feedback when the clipboard API is missing or the write fails.
2026-04-13 09:16:11 -07:00
Jordan Ritter 23ea810c5a fix: remove unstable key on CodeBlock to prevent flickering during streaming
The original Math.random() key caused React to remount the CodeBlock on
every render. The PR's content-based key (language + content prefix) still
changed every streaming token, causing the same flickering. Removing the
key entirely lets React use positional identity, which is stable across
re-renders while content streams in.

Closes #2669
2026-04-13 08:28:28 -07:00