@copilotkit/web-inspector 1.68.0 → 1.68.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -4,6 +4,7 @@ const require_inspector_logo = require('./assets/inspector-logo.cjs');
4
4
  const require_inspector_logo_icon = require('./assets/inspector-logo-icon.cjs');
5
5
  const require_context_helpers = require('./lib/context-helpers.cjs');
6
6
  const require_persistence = require('./lib/persistence.cjs');
7
+ const require_pop_out = require('./lib/pop-out.cjs');
7
8
  const require_inspector_metadata = require('./lib/inspector-metadata.cjs');
8
9
  const require_thread_selection = require('./lib/thread-selection.cjs');
9
10
  const require_telemetry = require('./lib/telemetry.cjs');
@@ -470,7 +471,24 @@ function formatTimestamp(ts) {
470
471
  hour12: false
471
472
  }) + "." + ms;
472
473
  }
473
- var CpkThreadList = class extends lit.LitElement {
474
+ /**
475
+ * Lit's constructable stylesheets belong to the document that created them.
476
+ * These child elements move between the app and pop-out documents, so keep
477
+ * their styles as shadow-root style nodes that travel with the live element.
478
+ */
479
+ var PortableLitElement = class extends lit.LitElement {
480
+ createRenderRoot() {
481
+ const elementClass = this.constructor;
482
+ const renderRoot = this.shadowRoot ?? this.attachShadow(elementClass.shadowRootOptions);
483
+ for (const style of elementClass.elementStyles) {
484
+ const styleElement = this.ownerDocument.createElement("style");
485
+ styleElement.textContent = "cssText" in style ? style.cssText : Array.from(style.cssRules, (rule) => rule.cssText).join("");
486
+ renderRoot.append(styleElement);
487
+ }
488
+ return renderRoot;
489
+ }
490
+ };
491
+ var CpkThreadList = class extends PortableLitElement {
474
492
  constructor(..._args) {
475
493
  super(..._args);
476
494
  this.threads = [];
@@ -768,7 +786,7 @@ var CpkThreadList = class extends lit.LitElement {
768
786
  `;
769
787
  }
770
788
  };
771
- var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
789
+ var CpkThreadInspector = class CpkThreadInspector extends PortableLitElement {
772
790
  constructor(..._args2) {
773
791
  super(..._args2);
774
792
  this.threadId = null;
@@ -780,6 +798,8 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
780
798
  this.agentStateInput = null;
781
799
  this.agentEventsInput = [];
782
800
  this.liveMessageVersion = 0;
801
+ this.focusMessageId = null;
802
+ this.focusRequestId = 0;
783
803
  this._tab = "timeline";
784
804
  this._fetchedMetadata = null;
785
805
  this._conversation = [];
@@ -799,6 +819,8 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
799
819
  this._detailPanelWidth = 250;
800
820
  this._eventsNotAvailable = false;
801
821
  this._stateNotAvailable = false;
822
+ this._scrolledFocusRequestId = 0;
823
+ this._highlightedFocusRequestId = -1;
802
824
  this._panelInitializing = false;
803
825
  this._activatedTabs = new Set(["timeline"]);
804
826
  this._panelTplCache = /* @__PURE__ */ new Map();
@@ -849,6 +871,8 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
849
871
  agentStateInput: { attribute: false },
850
872
  agentEventsInput: { attribute: false },
851
873
  liveMessageVersion: { attribute: false },
874
+ focusMessageId: { attribute: false },
875
+ focusRequestId: { attribute: false },
852
876
  _tab: { state: true },
853
877
  _fetchedMetadata: { state: true },
854
878
  _conversation: { state: true },
@@ -1223,6 +1247,37 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
1223
1247
  color: #c0333a;
1224
1248
  }
1225
1249
 
1250
+ @keyframes cpk-td-focus-pulse {
1251
+ 0% {
1252
+ outline-color: rgba(100, 48, 171, 0);
1253
+ box-shadow: 0 0 0 rgba(100, 48, 171, 0);
1254
+ animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
1255
+ }
1256
+ 24%,
1257
+ 50% {
1258
+ outline-color: rgba(100, 48, 171, 0.78);
1259
+ box-shadow: 0 7px 20px rgba(100, 48, 171, 0.2);
1260
+ }
1261
+ 100% {
1262
+ outline-color: rgba(100, 48, 171, 0);
1263
+ box-shadow: 0 0 0 rgba(100, 48, 171, 0);
1264
+ }
1265
+ }
1266
+
1267
+ .cpk-td__focus-pulse {
1268
+ position: relative;
1269
+ z-index: 1;
1270
+ outline: 2px solid transparent;
1271
+ outline-offset: 3px;
1272
+ animation: cpk-td-focus-pulse 760ms linear;
1273
+ }
1274
+
1275
+ @media (prefers-reduced-motion: reduce) {
1276
+ .cpk-td__focus-pulse {
1277
+ animation-duration: 320ms;
1278
+ }
1279
+ }
1280
+
1226
1281
  /* ── Conversation bubbles ────────────────────────────────────────── */
1227
1282
  .cpk-td__bubble {
1228
1283
  display: flex;
@@ -1758,6 +1813,31 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
1758
1813
  this._messagesAbort = null;
1759
1814
  this.fetchMessages(this.threadId, true);
1760
1815
  }
1816
+ const focusedContentChanged = _changed.has("_fetchedEvents") || _changed.has("agentEventsInput") || _changed.has("_conversation");
1817
+ if (this.focusMessageId && (this.focusRequestId > this._scrolledFocusRequestId || focusedContentChanged)) {
1818
+ if (this._tab !== "timeline") {
1819
+ this._activatedTabs = new Set([...this._activatedTabs, "timeline"]);
1820
+ this._tab = "timeline";
1821
+ this.requestUpdate();
1822
+ }
1823
+ requestAnimationFrame(() => this.scrollToFocusedMessage());
1824
+ }
1825
+ }
1826
+ scrollToFocusedMessage() {
1827
+ if (!this.focusMessageId) return;
1828
+ const message = Array.from(this.shadowRoot?.querySelectorAll("[data-message-id]") ?? []).find((candidate) => candidate.dataset.messageId === this.focusMessageId);
1829
+ if (!message) return;
1830
+ message.scrollIntoView?.({ block: "center" });
1831
+ this._scrolledFocusRequestId = this.focusRequestId;
1832
+ this.pulseFocusedMessage(message);
1833
+ }
1834
+ pulseFocusedMessage(message) {
1835
+ if (this.focusRequestId === this._highlightedFocusRequestId) return;
1836
+ this._highlightedFocusRequestId = this.focusRequestId;
1837
+ message.classList.remove("cpk-td__focus-pulse");
1838
+ message.offsetWidth;
1839
+ message.classList.add("cpk-td__focus-pulse");
1840
+ message.addEventListener("animationend", () => message.classList.remove("cpk-td__focus-pulse"), { once: true });
1761
1841
  }
1762
1842
  connectedCallback() {
1763
1843
  super.connectedCallback();
@@ -2105,6 +2185,7 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
2105
2185
  if (!item) {
2106
2186
  item = {
2107
2187
  id: `message-${key}`,
2188
+ messageId: key,
2108
2189
  kind: "message",
2109
2190
  title: `${role || "message"} message`,
2110
2191
  body: "",
@@ -2634,6 +2715,7 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
2634
2715
  return lit.html`
2635
2716
  <div
2636
2717
  class="cpk-td__timeline-item ${isWarning ? "cpk-td__timeline-item--warning" : ""}"
2718
+ data-message-id=${item.messageId ?? lit.nothing}
2637
2719
  >
2638
2720
  <div class="cpk-td__timeline-header">
2639
2721
  <span class="cpk-td__timeline-kind"
@@ -2770,6 +2852,7 @@ var CpkThreadInspector = class CpkThreadInspector extends lit.LitElement {
2770
2852
  return lit.html`
2771
2853
  <div
2772
2854
  class="cpk-td__bubble ${isUser ? "cpk-td__bubble--user" : "cpk-td__bubble--assistant"}"
2855
+ data-message-id=${item.id}
2773
2856
  >
2774
2857
  <div
2775
2858
  class="cpk-td__bubble-inner ${isUser ? "cpk-td__bubble-inner--user" : "cpk-td__bubble-inner--assistant"}"
@@ -3126,7 +3209,7 @@ function maxRecallScore(memories) {
3126
3209
  function relevanceBarWidth(relevance) {
3127
3210
  return Math.max(6, Math.min(100, Math.round(relevance * 100)));
3128
3211
  }
3129
- var CpkMemoryList = class extends lit.LitElement {
3212
+ var CpkMemoryList = class extends PortableLitElement {
3130
3213
  constructor(..._args3) {
3131
3214
  super(..._args3);
3132
3215
  this.memories = [];
@@ -3683,7 +3766,7 @@ var ɵCpkThreadDetails = class extends CpkThreadInspector {};
3683
3766
  function defineElementOnce(registry, tag, ctor) {
3684
3767
  if (!registry.get(tag)) registry.define(tag, ctor);
3685
3768
  }
3686
- var WebInspectorElement = class extends lit.LitElement {
3769
+ var WebInspectorElement = class WebInspectorElement extends lit.LitElement {
3687
3770
  constructor(..._args4) {
3688
3771
  super(..._args4);
3689
3772
  this._core = null;
@@ -3739,6 +3822,9 @@ var WebInspectorElement = class extends lit.LitElement {
3739
3822
  this.selectedThreadId = null;
3740
3823
  this.selectedRealThreadIsExplicit = false;
3741
3824
  this.selectedLocalExampleThreadId = null;
3825
+ this.requestedThreadId = null;
3826
+ this.focusedThreadMessageId = null;
3827
+ this.threadFocusRequestId = 0;
3742
3828
  this.threadListWidth = 290;
3743
3829
  this.threadDividerResizing = false;
3744
3830
  this.threadDividerPointerId = -1;
@@ -3754,6 +3840,8 @@ var WebInspectorElement = class extends lit.LitElement {
3754
3840
  this.threadCapabilityGeneration = 0;
3755
3841
  this.contextMenuOpen = false;
3756
3842
  this.dockMode = "floating";
3843
+ this.popOut = null;
3844
+ this.inspectorPortal = null;
3757
3845
  this.previousBodyMargins = null;
3758
3846
  this.transitionTimeoutId = null;
3759
3847
  this.bodyTransitionTimeoutIds = /* @__PURE__ */ new Set();
@@ -3934,6 +4022,45 @@ var WebInspectorElement = class extends lit.LitElement {
3934
4022
  this.handleCloseClick = () => {
3935
4023
  this.closeInspector();
3936
4024
  };
4025
+ this.requestPopOut = () => {
4026
+ if (this.isPoppedOut) return;
4027
+ const size = this.contextState.window.size;
4028
+ const handle = require_pop_out.openPopOutWindow({
4029
+ open: window.open.bind(window),
4030
+ features: require_pop_out.buildPopOutFeatures(size),
4031
+ title: "CopilotKit Inspector",
4032
+ cssTexts: this.getPopOutCssTexts(),
4033
+ sourceDocument: document,
4034
+ onClose: () => this.handlePopOutClosed()
4035
+ });
4036
+ this.popOut = handle;
4037
+ try {
4038
+ defineWebInspector(handle.win.customElements);
4039
+ if (this.dockMode !== "floating") this.removeDockStyles();
4040
+ handle.win.addEventListener("pointerdown", this.handleGlobalPointerDown);
4041
+ this.syncInspectorPortal();
4042
+ this.requestUpdate();
4043
+ } catch (error) {
4044
+ this.popOut = null;
4045
+ this.unbindPopOutPointerDown(handle.win);
4046
+ handle.close();
4047
+ if (this.isConnected && this.isOpen && this.dockMode !== "floating") this.applyDockStyles();
4048
+ this.requestUpdate();
4049
+ throw error;
4050
+ }
4051
+ };
4052
+ this.handlePopOutClosed = () => {
4053
+ const handle = this.popOut;
4054
+ if (!handle) return;
4055
+ this.popOut = null;
4056
+ this.unbindPopOutPointerDown(handle.win);
4057
+ this.syncInspectorPortal();
4058
+ if (this.isConnected && this.isOpen && this.dockMode !== "floating") this.applyDockStyles();
4059
+ this.requestUpdate();
4060
+ };
4061
+ this.handleAppBeforeUnload = () => {
4062
+ this.closePopOut();
4063
+ };
3937
4064
  this.handleResizePointerDown = (event) => {
3938
4065
  event.stopPropagation();
3939
4066
  event.preventDefault();
@@ -3994,6 +4121,7 @@ var WebInspectorElement = class extends lit.LitElement {
3994
4121
  this.resetResizeTracking();
3995
4122
  };
3996
4123
  this.handleResize = () => {
4124
+ if (this.isPoppedOut) return;
3997
4125
  this.measureContext("button");
3998
4126
  this.applyAnchorPosition("button");
3999
4127
  this.measureContext("window");
@@ -4087,7 +4215,7 @@ var WebInspectorElement = class extends lit.LitElement {
4087
4215
  }
4088
4216
  this.playThreadsExampleOverviewVideo(video, lifecycleGeneration);
4089
4217
  };
4090
- this.handleThreadsSetupPromptCopy = async () => {
4218
+ this.handleThreadsSetupPromptCopy = async (event) => {
4091
4219
  const generation = this.threadsSetupPromptCopyGeneration += 1;
4092
4220
  if (this.threadsSetupPromptCopyResetTimeoutId !== null) {
4093
4221
  window.clearTimeout(this.threadsSetupPromptCopyResetTimeoutId);
@@ -4095,12 +4223,13 @@ var WebInspectorElement = class extends lit.LitElement {
4095
4223
  }
4096
4224
  this.threadsSetupPromptCopyState = "idle";
4097
4225
  this.requestUpdate();
4098
- if (typeof navigator === "undefined" || !navigator.clipboard?.writeText) {
4226
+ const clipboard = this.getClipboard(event);
4227
+ if (!clipboard?.writeText) {
4099
4228
  this.showThreadsSetupPromptCopyState("error", generation);
4100
4229
  return;
4101
4230
  }
4102
4231
  try {
4103
- await navigator.clipboard.writeText(THREADS_RUNTIME_SETUP_PROMPT);
4232
+ await clipboard.writeText(THREADS_RUNTIME_SETUP_PROMPT);
4104
4233
  this.showThreadsSetupPromptCopyState("copied", generation);
4105
4234
  } catch {
4106
4235
  this.showThreadsSetupPromptCopyState("error", generation);
@@ -4121,7 +4250,8 @@ var WebInspectorElement = class extends lit.LitElement {
4121
4250
  this.handleGlobalPointerDown = (event) => {
4122
4251
  if (!this.contextMenuOpen) return;
4123
4252
  if (!event.composedPath().some((node) => {
4124
- return node instanceof HTMLElement && node.dataset?.contextDropdownRoot === "true";
4253
+ const candidate = node;
4254
+ return typeof candidate.getAttribute === "function" && candidate.getAttribute("data-context-dropdown-root") === "true";
4125
4255
  })) {
4126
4256
  this.contextMenuOpen = false;
4127
4257
  this.requestUpdate();
@@ -4133,8 +4263,10 @@ var WebInspectorElement = class extends lit.LitElement {
4133
4263
  };
4134
4264
  this.copyResetTimeouts = /* @__PURE__ */ new WeakMap();
4135
4265
  this.handleAnnouncementContentClick = (event) => {
4136
- const button = (event.target instanceof HTMLElement ? event.target : null)?.closest(".announcement-code__copy");
4137
- if (!(button instanceof HTMLButtonElement)) {
4266
+ const target = event.target;
4267
+ const closest = typeof target?.closest === "function" ? target.closest(".announcement-code__copy") : null;
4268
+ const button = closest?.tagName === "BUTTON" ? closest : null;
4269
+ if (!button) {
4138
4270
  this.trackBannerClickedOnce({ cta: "body" });
4139
4271
  return;
4140
4272
  }
@@ -4144,12 +4276,13 @@ var WebInspectorElement = class extends lit.LitElement {
4144
4276
  const code = this.decodeBase64(encoded);
4145
4277
  if (!code) return;
4146
4278
  const showCopied = () => {
4279
+ const view = button.ownerDocument.defaultView ?? window;
4147
4280
  const existing = this.copyResetTimeouts.get(button);
4148
- if (existing !== void 0) window.clearTimeout(existing);
4281
+ if (existing !== void 0) view.clearTimeout(existing);
4149
4282
  button.setAttribute("data-copied", "true");
4150
4283
  button.setAttribute("aria-label", "Code copied");
4151
4284
  button.textContent = "Copied";
4152
- const id = window.setTimeout(() => {
4285
+ const id = view.setTimeout(() => {
4153
4286
  button.removeAttribute("data-copied");
4154
4287
  button.setAttribute("aria-label", "Copy code");
4155
4288
  button.textContent = "Copy";
@@ -4157,7 +4290,8 @@ var WebInspectorElement = class extends lit.LitElement {
4157
4290
  }, 1500);
4158
4291
  this.copyResetTimeouts.set(button, id);
4159
4292
  };
4160
- if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) navigator.clipboard.writeText(code).then(showCopied, () => {});
4293
+ const clipboard = this.getClipboard(event);
4294
+ if (clipboard?.writeText) clipboard.writeText(code).then(showCopied, () => {});
4161
4295
  };
4162
4296
  }
4163
4297
  static {
@@ -4440,6 +4574,12 @@ var WebInspectorElement = class extends lit.LitElement {
4440
4574
  if (!this.areThreadEndpointsAvailable()) return;
4441
4575
  const { displayThreads } = this.getActiveThreadsState();
4442
4576
  const previousSelectedThreadId = this.selectedThreadId;
4577
+ if (this.requestedThreadId !== null) {
4578
+ this.selectedThreadId = this.requestedThreadId;
4579
+ this.selectedRealThreadIsExplicit = true;
4580
+ if (displayThreads.some((thread) => thread.id === this.requestedThreadId)) this.requestedThreadId = null;
4581
+ return;
4582
+ }
4443
4583
  if (this.selectedLocalExampleThreadId !== null && previousSelectedThreadId === this.selectedLocalExampleThreadId && displayThreads.length === 0) {
4444
4584
  this.selectedRealThreadIsExplicit = false;
4445
4585
  return;
@@ -5049,6 +5189,26 @@ var WebInspectorElement = class extends lit.LitElement {
5049
5189
  if (this.selectedContext === "all-agents") return this.flattenedEvents;
5050
5190
  return this.agentEvents.get(this.selectedContext) ?? [];
5051
5191
  }
5192
+ focusThread(options) {
5193
+ if (!options.threadId) return;
5194
+ this.pendingPersistedMenu = null;
5195
+ this.selectedMenu = "threads";
5196
+ this.settingsOpen = false;
5197
+ this.lastSelectedMenuByGroup.threads = "threads";
5198
+ this.contextMenuOpen = false;
5199
+ this.selectedLocalExampleThreadId = null;
5200
+ this.exampleTourActive = false;
5201
+ this.selectedContext = options.agentId && this.contextOptions.some((option) => option.key === options.agentId) ? options.agentId : "all-agents";
5202
+ this.requestedThreadId = options.threadId;
5203
+ this.selectedThreadId = options.threadId;
5204
+ this.selectedRealThreadIsExplicit = true;
5205
+ this.focusedThreadMessageId = options.messageId ?? null;
5206
+ this.threadFocusRequestId += 1;
5207
+ const { displayThreads } = this.getActiveThreadsState();
5208
+ if (displayThreads.some((thread) => thread.id === options.threadId)) this.requestedThreadId = null;
5209
+ this.persistState();
5210
+ this.requestUpdate();
5211
+ }
5052
5212
  filterEvents(events) {
5053
5213
  const query = this.eventFilterText.trim().toLowerCase();
5054
5214
  return events.filter((event) => {
@@ -5190,9 +5350,24 @@ ${argsString}</pre
5190
5350
  if (payload && typeof payload === "object" && "event" in payload) return payload.event;
5191
5351
  return payload;
5192
5352
  }
5193
- async copyToClipboard(text, eventId) {
5353
+ /** Prefer the window that owns the UI: the popup while popped out. */
5354
+ getClipboard(event) {
5355
+ if (this.isPoppedOut) {
5356
+ const popped = this.popOut?.win.navigator.clipboard;
5357
+ if (popped) return popped;
5358
+ }
5359
+ const viewClipboard = (event && "view" in event ? event.view : null)?.navigator.clipboard;
5360
+ if (viewClipboard) return viewClipboard;
5361
+ if (typeof navigator !== "undefined" && "clipboard" in navigator) return navigator.clipboard;
5362
+ }
5363
+ async copyToClipboard(text, eventId, event) {
5364
+ const clipboard = this.getClipboard(event);
5365
+ if (!clipboard) {
5366
+ console.error("Failed to copy to clipboard:", "Clipboard API is not available");
5367
+ return;
5368
+ }
5194
5369
  try {
5195
- await navigator.clipboard.writeText(text);
5370
+ await clipboard.writeText(text);
5196
5371
  this.copiedEvents.add(eventId);
5197
5372
  this.requestUpdate();
5198
5373
  setTimeout(() => {
@@ -6010,6 +6185,9 @@ ${argsString}</pre
6010
6185
  this.ensureBrandFonts();
6011
6186
  window.addEventListener("resize", this.handleResize);
6012
6187
  window.addEventListener("pointerdown", this.handleGlobalPointerDown);
6188
+ window.addEventListener("beforeunload", this.handleAppBeforeUnload);
6189
+ const viteHot = {}.hot;
6190
+ if (viteHot) viteHot.on("vite:beforeUpdate", this.handleAppBeforeUnload);
6013
6191
  this.hydrateStateFromStorageEarly();
6014
6192
  this.exampleTourDismissed = this.readThreadsExampleTourDismissed();
6015
6193
  this.tryAutoAttachCore();
@@ -6018,19 +6196,15 @@ ${argsString}</pre
6018
6196
  this.requestUpdate();
6019
6197
  }
6020
6198
  ensureBrandFonts() {
6021
- const FONT_LINK_ID = "cpk-inspector-brand-fonts";
6022
- if (document.getElementById(FONT_LINK_ID)) return;
6023
- const link = document.createElement("link");
6024
- link.id = FONT_LINK_ID;
6025
- link.rel = "stylesheet";
6026
- link.href = "https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600&family=Spline+Sans+Mono:wght@600&display=swap";
6027
- document.head.appendChild(link);
6199
+ require_pop_out.ensureBrandFont(document);
6028
6200
  }
6029
6201
  disconnectedCallback() {
6030
6202
  super.disconnectedCallback();
6203
+ this.closePopOut();
6031
6204
  if (typeof window !== "undefined") {
6032
6205
  window.removeEventListener("resize", this.handleResize);
6033
6206
  window.removeEventListener("pointerdown", this.handleGlobalPointerDown);
6207
+ window.removeEventListener("beforeunload", this.handleAppBeforeUnload);
6034
6208
  }
6035
6209
  for (const id of this.bodyTransitionTimeoutIds) clearTimeout(id);
6036
6210
  this.bodyTransitionTimeoutIds.clear();
@@ -6080,19 +6254,22 @@ ${argsString}</pre
6080
6254
  this.updateHostTransform(this.isOpen ? "window" : "button");
6081
6255
  }
6082
6256
  render() {
6083
- return this.isOpen ? this.renderWindow() : this.renderButton();
6257
+ return this.isOpen ? lit.html`
6258
+ <div data-inspector-portal-anchor></div>
6259
+ ` : this.renderButton();
6084
6260
  }
6085
6261
  willUpdate() {
6086
6262
  this.reconcileSelectedMenuVisibility();
6087
6263
  }
6088
6264
  updated() {
6265
+ this.syncInspectorPortal();
6089
6266
  this.syncThreadsExampleOverviewVideo();
6090
6267
  this.maybeTrackInspectorMetadataViews();
6091
6268
  if (!this.isOpen || this.selectedGroup !== "agents") {
6092
6269
  this.lastScrolledAgentNavigationLayout = null;
6093
6270
  return;
6094
6271
  }
6095
- const navigation = this.shadowRoot?.querySelector("nav[aria-label=\"Agent navigation\"]");
6272
+ const navigation = this.activeRoot.querySelector("nav[aria-label=\"Agent navigation\"]");
6096
6273
  if (!navigation) return;
6097
6274
  const activeControl = navigation.querySelector("[aria-current=\"page\"]");
6098
6275
  if (!activeControl) return;
@@ -6222,8 +6399,19 @@ ${argsString}</pre
6222
6399
  renderWindow() {
6223
6400
  const windowState = this.contextState.window;
6224
6401
  const isDocked = this.dockMode !== "floating";
6402
+ const isPoppedOut = this.isPoppedOut;
6225
6403
  const isTransitioning = this.hasAttribute("data-transitioning");
6226
- const windowStyles = isDocked ? {
6404
+ const disableDrag = isDocked || isPoppedOut;
6405
+ const windowStyles = isPoppedOut ? {
6406
+ position: "fixed",
6407
+ inset: "0",
6408
+ width: "100%",
6409
+ height: "100%",
6410
+ minWidth: "0",
6411
+ minHeight: "0",
6412
+ borderRadius: "0",
6413
+ overflowX: "hidden"
6414
+ } : isDocked ? {
6227
6415
  ...this.getDockedWindowStyles(),
6228
6416
  overflowX: "hidden"
6229
6417
  } : {
@@ -6251,7 +6439,7 @@ ${argsString}</pre
6251
6439
  data-docked=${isDocked}
6252
6440
  data-transitioning=${isTransitioning}
6253
6441
  >
6254
- ${isDocked ? lit.html`
6442
+ ${isDocked && !isPoppedOut ? lit.html`
6255
6443
  <div
6256
6444
  class="dock-resize-handle pointer-events-auto"
6257
6445
  role="presentation"
@@ -6266,12 +6454,12 @@ ${argsString}</pre
6266
6454
  class="flex flex-1 flex-col overflow-hidden bg-white text-gray-800"
6267
6455
  >
6268
6456
  <div
6269
- class="drag-handle relative z-30 flex flex-col border-b border-gray-200 bg-white/95 backdrop-blur-sm ${isDocked ? "" : this.isDragging && this.pointerContext === "window" ? "cursor-grabbing" : "cursor-grab"}"
6457
+ class="drag-handle relative z-30 flex flex-col border-b border-gray-200 bg-white/95 backdrop-blur-sm ${disableDrag ? "" : this.isDragging && this.pointerContext === "window" ? "cursor-grabbing" : "cursor-grab"}"
6270
6458
  data-drag-context="window"
6271
- @pointerdown=${isDocked ? void 0 : this.handlePointerDown}
6272
- @pointermove=${isDocked ? void 0 : this.handlePointerMove}
6273
- @pointerup=${isDocked ? void 0 : this.handlePointerUp}
6274
- @pointercancel=${isDocked ? void 0 : this.handlePointerCancel}
6459
+ @pointerdown=${disableDrag ? void 0 : this.handlePointerDown}
6460
+ @pointermove=${disableDrag ? void 0 : this.handlePointerMove}
6461
+ @pointerup=${disableDrag ? void 0 : this.handlePointerUp}
6462
+ @pointercancel=${disableDrag ? void 0 : this.handlePointerCancel}
6275
6463
  >
6276
6464
  <div
6277
6465
  class="inspector-account-strip flex flex-wrap items-center gap-3 px-4 py-3"
@@ -6292,27 +6480,46 @@ ${argsString}</pre
6292
6480
  ${agentSelector}
6293
6481
  </div>
6294
6482
  <div class="flex items-center gap-1">
6295
- ${this.renderDockControls()}
6483
+ ${isPoppedOut ? lit.nothing : lit.html`
6484
+ ${this.renderDockControls()}
6485
+ <button
6486
+ class="inspector-account-control flex h-8 w-8 items-center justify-center rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
6487
+ type="button"
6488
+ aria-label="Detach Inspector into its own window"
6489
+ title="Detach into its own window"
6490
+ data-testid="cpk-inspector-pop-out"
6491
+ style=${INTERACTIVE_FOCUS_BASE_STYLE}
6492
+ .click=${this.requestPopOut}
6493
+ @click=${this.requestPopOut}
6494
+ >
6495
+ ${this.renderIcon("PictureInPicture2")}
6496
+ </button>
6497
+ `}
6296
6498
  <button
6297
6499
  class="inspector-account-control flex h-8 w-8 items-center justify-center rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 ${this.settingsOpen ? "inspector-account-control-active" : ""}"
6298
6500
  type="button"
6299
6501
  aria-label="Settings"
6300
6502
  aria-pressed=${this.settingsOpen}
6503
+ title="Settings"
6301
6504
  style=${INTERACTIVE_FOCUS_BASE_STYLE}
6302
6505
  @click=${this.handleSettingsToggle}
6303
6506
  >
6304
- ${this.renderIcon("Settings")}
6305
- </button>
6306
- <button
6307
- class="inspector-account-control flex h-8 w-8 items-center justify-center rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
6308
- type="button"
6309
- aria-label="Close Web Inspector"
6310
- style=${INTERACTIVE_FOCUS_BASE_STYLE}
6311
- @pointerdown=${this.handleClosePointerDown}
6312
- @click=${this.handleCloseClick}
6313
- >
6314
- ${this.renderIcon("X")}
6507
+ <span class="inspector-account-control-icon" aria-hidden="true">
6508
+ ${this.renderIcon("Settings")}
6509
+ </span>
6315
6510
  </button>
6511
+ ${isPoppedOut ? lit.nothing : lit.html`
6512
+ <button
6513
+ class="inspector-account-control flex h-8 w-8 items-center justify-center rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
6514
+ type="button"
6515
+ aria-label="Close Web Inspector"
6516
+ style=${INTERACTIVE_FOCUS_BASE_STYLE}
6517
+ @pointerdown=${this.handleClosePointerDown}
6518
+ @click=${this.handleCloseClick}
6519
+ >
6520
+ ${this.renderIcon("X")}
6521
+ </button>
6522
+ `}
6316
6523
  </div>
6317
6524
  </div>
6318
6525
  </div>
@@ -6407,27 +6614,29 @@ ${argsString}</pre
6407
6614
  </div>
6408
6615
  </div>
6409
6616
  </div>
6410
- <div
6411
- class="resize-handle pointer-events-auto absolute bottom-1 right-1 flex h-5 w-5 cursor-nwse-resize items-center justify-center text-gray-400 transition hover:text-gray-600"
6412
- role="presentation"
6413
- aria-hidden="true"
6414
- @pointerdown=${this.handleResizePointerDown}
6415
- @pointermove=${this.handleResizePointerMove}
6416
- @pointerup=${this.handleResizePointerUp}
6417
- @pointercancel=${this.handleResizePointerCancel}
6418
- >
6419
- <svg
6420
- class="h-3 w-3"
6421
- viewBox="0 0 16 16"
6422
- fill="none"
6423
- stroke="currentColor"
6424
- stroke-linecap="round"
6425
- stroke-width="1.5"
6426
- >
6427
- <path d="M5 15L15 5" />
6428
- <path d="M9 15L15 9" />
6429
- </svg>
6430
- </div>
6617
+ ${isPoppedOut ? lit.nothing : lit.html`
6618
+ <div
6619
+ class="resize-handle pointer-events-auto absolute bottom-1 right-1 flex h-5 w-5 cursor-nwse-resize items-center justify-center text-gray-400 transition hover:text-gray-600"
6620
+ role="presentation"
6621
+ aria-hidden="true"
6622
+ @pointerdown=${this.handleResizePointerDown}
6623
+ @pointermove=${this.handleResizePointerMove}
6624
+ @pointerup=${this.handleResizePointerUp}
6625
+ @pointercancel=${this.handleResizePointerCancel}
6626
+ >
6627
+ <svg
6628
+ class="h-3 w-3"
6629
+ viewBox="0 0 16 16"
6630
+ fill="none"
6631
+ stroke="currentColor"
6632
+ stroke-linecap="round"
6633
+ stroke-width="1.5"
6634
+ >
6635
+ <path d="M5 15L15 5" />
6636
+ <path d="M9 15L15 9" />
6637
+ </svg>
6638
+ </div>
6639
+ `}
6431
6640
  </section>
6432
6641
  `;
6433
6642
  }
@@ -6491,6 +6700,62 @@ ${argsString}</pre
6491
6700
  get activeContext() {
6492
6701
  return this.isOpen ? "window" : "button";
6493
6702
  }
6703
+ get isPoppedOut() {
6704
+ return this.popOut !== null && !this.popOut.win.closed;
6705
+ }
6706
+ get activeRoot() {
6707
+ if (this.isPoppedOut) return this.popOut.win.document;
6708
+ return this.renderRoot;
6709
+ }
6710
+ getPopOutCssTexts() {
6711
+ const fromStatic = WebInspectorElement.styles.map((sheet) => "cssText" in sheet ? String(sheet.cssText) : "");
6712
+ const overlay = `
6713
+ html, body { margin: 0; height: 100%; background: #ffffff; }
6714
+ .inspector-window {
6715
+ position: fixed !important;
6716
+ inset: 0 !important;
6717
+ width: 100% !important;
6718
+ height: 100% !important;
6719
+ min-width: 0 !important;
6720
+ min-height: 0 !important;
6721
+ border-radius: 0 !important;
6722
+ }
6723
+ `;
6724
+ return [...fromStatic.filter(Boolean), overlay];
6725
+ }
6726
+ unbindPopOutPointerDown(win) {
6727
+ try {
6728
+ win.removeEventListener("pointerdown", this.handleGlobalPointerDown);
6729
+ } catch {}
6730
+ }
6731
+ closePopOut() {
6732
+ const handle = this.popOut;
6733
+ if (!handle) return;
6734
+ this.handlePopOutClosed();
6735
+ handle.close();
6736
+ }
6737
+ syncInspectorPortal() {
6738
+ if (!this.inspectorPortal) {
6739
+ const portal = (this.ownerDocument ?? document).createElement("div");
6740
+ portal.dataset.inspectorPortal = "true";
6741
+ portal.style.display = "contents";
6742
+ this.inspectorPortal = portal;
6743
+ }
6744
+ if (!this.isOpen) {
6745
+ (0, lit.render)(lit.nothing, this.inspectorPortal, {
6746
+ host: this,
6747
+ creationScope: this.ownerDocument ?? document
6748
+ });
6749
+ this.inspectorPortal.remove();
6750
+ return;
6751
+ }
6752
+ (0, lit.render)(this.renderWindow(), this.inspectorPortal, {
6753
+ host: this,
6754
+ creationScope: this.ownerDocument ?? document
6755
+ });
6756
+ const target = this.isPoppedOut ? this.popOut.win.document.body : this.renderRoot.querySelector("[data-inspector-portal-anchor]");
6757
+ if (target && this.inspectorPortal.parentNode !== target) target.appendChild(this.inspectorPortal);
6758
+ }
6494
6759
  measureContext(context) {
6495
6760
  const selector = context === "window" ? ".inspector-window" : ".console-button";
6496
6761
  const element = this.renderRoot?.querySelector(selector);
@@ -6628,6 +6893,7 @@ ${argsString}</pre
6628
6893
  } else document.body.style.transition = "";
6629
6894
  }
6630
6895
  updateHostTransform(context = this.activeContext) {
6896
+ if (this.isPoppedOut) return;
6631
6897
  if (context !== this.activeContext) return;
6632
6898
  if (this.isOpen && this.dockMode === "docked-left") this.style.transform = `translate3d(0, 0, 0)`;
6633
6899
  else {
@@ -6664,6 +6930,7 @@ ${argsString}</pre
6664
6930
  this.applyAnchorPosition("button");
6665
6931
  }
6666
6932
  applyAnchorPosition(context) {
6933
+ if (this.isPoppedOut) return;
6667
6934
  if (typeof window === "undefined") return;
6668
6935
  const viewport = this.getViewportSize();
6669
6936
  require_context_helpers.applyAnchorPosition(this.contextState[context], viewport, EDGE_MARGIN);
@@ -6683,7 +6950,9 @@ ${argsString}</pre
6683
6950
  this.setDragging(false);
6684
6951
  this.draggedDuringInteraction = false;
6685
6952
  }
6686
- openInspector(source) {
6953
+ openInspector(source, options = {}) {
6954
+ if (options.threadId) this.focusThread(options);
6955
+ if (this.isPoppedOut) return;
6687
6956
  if (this.isOpen) return;
6688
6957
  const hadUnseenAnnouncement = this.hasUnseenAnnouncement;
6689
6958
  this.showAnnouncementPreview = false;
@@ -6703,6 +6972,7 @@ ${argsString}</pre
6703
6972
  });
6704
6973
  }
6705
6974
  closeInspector() {
6975
+ if (this.isPoppedOut) return;
6706
6976
  if (!this.isOpen) return;
6707
6977
  this.isOpen = false;
6708
6978
  if (this.dockMode !== "floating") this.removeDockStyles();
@@ -7178,7 +7448,7 @@ ${argsString}</pre
7178
7448
  const step = THREADS_EXAMPLE_TOUR_STEPS[this.exampleTourStep] ?? THREADS_EXAMPLE_TOUR_STEPS[0];
7179
7449
  if (!step) return;
7180
7450
  this.updateComplete.then(() => {
7181
- (this.shadowRoot?.querySelector("cpk-thread-details"))?.selectTab?.(step.tab);
7451
+ this.activeRoot.querySelector("cpk-thread-details")?.selectTab?.(step.tab);
7182
7452
  });
7183
7453
  }
7184
7454
  startExampleTour(autoStarted) {
@@ -7225,6 +7495,8 @@ ${argsString}</pre
7225
7495
  this.requestUpdate();
7226
7496
  }
7227
7497
  handleThreadsThreadSelected(threadId, showingExamples) {
7498
+ this.requestedThreadId = null;
7499
+ this.focusedThreadMessageId = null;
7228
7500
  if (showingExamples && this.selectedThreadId === threadId && this.selectedLocalExampleThreadId === threadId) {
7229
7501
  this.selectedThreadId = null;
7230
7502
  this.selectedRealThreadIsExplicit = false;
@@ -7418,7 +7690,7 @@ ${argsString}</pre
7418
7690
  }
7419
7691
  /** Reconciles the rendered media node with its current lifecycle. */
7420
7692
  syncThreadsExampleOverviewVideo() {
7421
- const video = this.shadowRoot?.querySelector(".cpk-threads-overview-video");
7693
+ const video = this.activeRoot.querySelector(".cpk-threads-overview-video");
7422
7694
  if (!video) {
7423
7695
  if (this.threadsExampleOverviewVideoElement || this.threadsExampleOverviewVideoState !== "deferred" || this.threadsExampleOverviewVideoLoadTimer !== null || this.threadsExampleOverviewVideoIdleCallbackId !== null || this.threadsExampleOverviewVideoPlayOnNextBind) this.cleanupThreadsExampleOverviewVideo();
7424
7696
  return;
@@ -8330,6 +8602,8 @@ ${argsString}</pre
8330
8602
  .headers=${this._core?.headers ?? {}}
8331
8603
  .threadInspectionAvailable=${selectedThreadIsLocalExample || this.areThreadEndpointsAvailable() && this._core?.threadEndpoints?.inspect !== false}
8332
8604
  .liveMessageVersion=${this.liveMessageVersion.get(selectedThread.id) ?? 0}
8605
+ .focusMessageId=${this.focusedThreadMessageId}
8606
+ .focusRequestId=${this.threadFocusRequestId}
8333
8607
  .agentStateInput=${this.getLatestStateForAgent(selectedThread.agentId)}
8334
8608
  .agentEventsInput=${this.agentEvents.get(selectedThread.agentId) ?? []}
8335
8609
  ></cpk-thread-details>
@@ -8519,7 +8793,7 @@ ${argsString}</pre
8519
8793
  return lit.html`
8520
8794
  <tr
8521
8795
  class="${rowBg} cursor-pointer transition hover:bg-blue-50/50"
8522
- @click=${() => this.toggleRowExpansion(event.id)}
8796
+ @click=${(clickEvent) => this.toggleRowExpansion(event.id, clickEvent)}
8523
8797
  >
8524
8798
  <td
8525
8799
  class="border-l border-r border-b border-gray-200 px-3 py-2"
@@ -8552,7 +8826,7 @@ ${prettyEvent}</pre
8552
8826
  class="absolute right-0 top-0 cursor-pointer rounded px-2 py-1 text-[10px] opacity-0 transition group-hover:opacity-100 ${this.copiedEvents.has(event.id) ? "bg-green-100 text-green-700" : "bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-900"}"
8553
8827
  @click=${(e) => {
8554
8828
  e.stopPropagation();
8555
- this.copyToClipboard(prettyEvent, event.id);
8829
+ this.copyToClipboard(prettyEvent, event.id, e);
8556
8830
  }}
8557
8831
  >
8558
8832
  ${this.copiedEvents.has(event.id) ? lit.html`
@@ -8897,7 +9171,7 @@ ${prettyEvent}</pre
8897
9171
  if (previousMenu !== "memories" && !this.core?.telemetryDisabled) require_telemetry.trackMemoriesTabClicked(this.getMemoriesTelemetryProps());
8898
9172
  }
8899
9173
  if (key === "ag-ui-events" || key === "agents") requestAnimationFrame(() => {
8900
- const scroller = this.shadowRoot?.getElementById("cpk-main-scroll");
9174
+ const scroller = this.activeRoot.querySelector("#cpk-main-scroll");
8901
9175
  if (scroller) scroller.scrollTop = 0;
8902
9176
  });
8903
9177
  this.contextMenuOpen = false;
@@ -9402,7 +9676,7 @@ ${prettyEvent}</pre
9402
9676
  class="cpk-copy-btn"
9403
9677
  @click=${(e) => {
9404
9678
  e.stopPropagation();
9405
- this.copyContextValue(id, `${id}:id`);
9679
+ this.copyContextValue(id, `${id}:id`, e);
9406
9680
  }}
9407
9681
  >
9408
9682
  ${this.copiedContextItems.has(`${id}:id`) ? "✓" : "Copy"}
@@ -9418,7 +9692,7 @@ ${prettyEvent}</pre
9418
9692
  class="cpk-copy-btn"
9419
9693
  @click=${(e) => {
9420
9694
  e.stopPropagation();
9421
- this.copyContextValue(context.value, id);
9695
+ this.copyContextValue(context.value, id, e);
9422
9696
  }}
9423
9697
  >
9424
9698
  ${this.copiedContextItems.has(id) ? "Copied" : "Copy JSON"}
@@ -9472,14 +9746,15 @@ ${prettyEvent}</pre
9472
9746
  return String(value);
9473
9747
  }
9474
9748
  }
9475
- async copyContextValue(value, contextId) {
9476
- if (typeof navigator === "undefined" || !navigator.clipboard?.writeText) {
9749
+ async copyContextValue(value, contextId, event) {
9750
+ const clipboard = this.getClipboard(event);
9751
+ if (!clipboard?.writeText) {
9477
9752
  console.warn("Clipboard API is not available in this environment.");
9478
9753
  return;
9479
9754
  }
9480
9755
  const serialized = this.formatContextValue(value);
9481
9756
  try {
9482
- await navigator.clipboard.writeText(serialized);
9757
+ await clipboard.writeText(serialized);
9483
9758
  this.copiedContextItems.add(contextId);
9484
9759
  this.requestUpdate();
9485
9760
  setTimeout(() => {
@@ -9495,8 +9770,9 @@ ${prettyEvent}</pre
9495
9770
  else this.expandedContextItems.add(contextId);
9496
9771
  this.requestUpdate();
9497
9772
  }
9498
- toggleRowExpansion(eventId) {
9499
- const selection = window.getSelection();
9773
+ toggleRowExpansion(eventId, event) {
9774
+ const target = event?.currentTarget;
9775
+ const selection = (target && "ownerDocument" in target ? target.ownerDocument : this.ownerDocument)?.getSelection();
9500
9776
  if (selection && selection.toString().length > 0) return;
9501
9777
  if (this.expandedRows.has(eventId)) this.expandedRows.delete(eventId);
9502
9778
  else this.expandedRows.add(eventId);