@aparte/core 0.17.0 → 0.17.1

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.
@@ -1318,6 +1318,7 @@ class AparteConfig {
1318
1318
  // Tool Registry
1319
1319
  _tools = /* @__PURE__ */ new Map();
1320
1320
  _toolRenderers = /* @__PURE__ */ new Map();
1321
+ _toolSummaries = /* @__PURE__ */ new Map();
1321
1322
  /** Tagged blocks the stream parser recognises in the prose — see {@link registerStreamBlock}. */
1322
1323
  _streamBlocks = /* @__PURE__ */ new Map();
1323
1324
  _segmentDefaults = /* @__PURE__ */ new Map();
@@ -2174,6 +2175,37 @@ class AparteConfig {
2174
2175
  getToolRenderer(toolName) {
2175
2176
  return this._toolRenderers.get(toolName);
2176
2177
  }
2178
+ /**
2179
+ * Say what a call targets, in the default row, after the tool's name.
2180
+ *
2181
+ * The default row names the tool and keeps its arguments behind a disclosure, so a
2182
+ * reader saw that a file was read, not which one. Replacing the row with
2183
+ * `registerToolRenderer` for that one line meant re-implementing its six states,
2184
+ * the spinner, the disclosure and the relabel contract. This keeps all of them and
2185
+ * adds the line. A tool with its own renderer draws its own row, so its summary is
2186
+ * never read.
2187
+ *
2188
+ * The text is written as text, never parsed as HTML. It is read when the row
2189
+ * renders, on every update to the call (its arguments may still be arriving), and
2190
+ * on every config change — so registering one after a row is on screen shows up on
2191
+ * that row's next update.
2192
+ *
2193
+ * @example
2194
+ * // read_file skills.json
2195
+ * aparteGlobalConfig.registerToolSummary('read_file', (segment) =>
2196
+ * String(segment.toolCall.input['path'] ?? ''));
2197
+ */
2198
+ registerToolSummary(toolName, summary) {
2199
+ this._toolSummaries.set(toolName, summary);
2200
+ }
2201
+ /** Unregister a per-tool summary. */
2202
+ unregisterToolSummary(toolName) {
2203
+ this._toolSummaries.delete(toolName);
2204
+ }
2205
+ /** Get the summary for a specific tool name. Returns undefined if none registered. */
2206
+ getToolSummary(toolName) {
2207
+ return this._toolSummaries.get(toolName);
2208
+ }
2177
2209
  /**
2178
2210
  * Teach the stream parser a tagged block: `<tag attr="…">…</tag>` in the model's
2179
2211
  * prose becomes the segment `toSegment` builds, streamed delta by delta.
@@ -2382,6 +2414,7 @@ class AparteConfig {
2382
2414
  this._fetchedModels.clear();
2383
2415
  this._tools.clear();
2384
2416
  this._toolRenderers.clear();
2417
+ this._toolSummaries.clear();
2385
2418
  this._streamBlocks.clear();
2386
2419
  this._modelConfig = {};
2387
2420
  this._requireModelSelection = false;
@@ -2467,6 +2500,7 @@ function subscribeConfigChange(el2, handler) {
2467
2500
  window.addEventListener(APARTE_CONFIG_CHANGE, listener);
2468
2501
  return () => window.removeEventListener(APARTE_CONFIG_CHANGE, listener);
2469
2502
  }
2503
+ const settledFromParser = /* @__PURE__ */ new WeakMap();
2470
2504
  const ONE_SHOT_PROBE = "<i>a</i>";
2471
2505
  function hasOneShotMarkdownProvider() {
2472
2506
  return contextConfig().renderMarkdown(ONE_SHOT_PROBE) !== escapeHtml(ONE_SHOT_PROBE);
@@ -2474,6 +2508,7 @@ function hasOneShotMarkdownProvider() {
2474
2508
  function writeStreamedMarkdown(host, contentEl, content, streaming) {
2475
2509
  if (streaming) {
2476
2510
  if (host._aparteSmd === void 0) {
2511
+ settledFromParser.delete(contentEl);
2477
2512
  const renderer = contextConfig().createStreamingMarkdownRenderer(contentEl);
2478
2513
  if (renderer) {
2479
2514
  contentEl.textContent = "";
@@ -2502,9 +2537,13 @@ function writeStreamedMarkdown(host, contentEl, content, streaming) {
2502
2537
  host._aparteSmd = void 0;
2503
2538
  if (!hasOneShotMarkdownProvider()) {
2504
2539
  contentEl.innerHTML = contextConfig().sanitizeHtml(contentEl.innerHTML);
2540
+ settledFromParser.set(contentEl, content);
2505
2541
  return;
2506
2542
  }
2543
+ } else if (settledFromParser.get(contentEl) === content && !hasOneShotMarkdownProvider()) {
2544
+ return;
2507
2545
  }
2546
+ settledFromParser.delete(contentEl);
2508
2547
  contentEl.innerHTML = contextConfig().renderMarkdown(content);
2509
2548
  }
2510
2549
  function highlightMarkdownFences(contentEl) {
@@ -2843,12 +2882,31 @@ function highlightParts(element) {
2843
2882
  });
2844
2883
  }
2845
2884
  }
2885
+ function toolTarget(segment) {
2886
+ const summary = contextConfig().getToolSummary(segment.toolCall?.name ?? "");
2887
+ if (!summary) return "";
2888
+ try {
2889
+ const text = summary(segment);
2890
+ return typeof text === "string" ? text.trim() : "";
2891
+ } catch {
2892
+ return "";
2893
+ }
2894
+ }
2895
+ function setTarget(element, segment) {
2896
+ const el2 = element.querySelector(".aparte-tool-target");
2897
+ if (!el2) return;
2898
+ const text = toolTarget(segment);
2899
+ if (el2.textContent !== text) el2.textContent = text;
2900
+ el2.toggleAttribute("hidden", !text);
2901
+ }
2846
2902
  function pillMarkup(segment, name, status) {
2847
2903
  const badge = stateBadge(segment);
2904
+ const target = toolTarget(segment);
2848
2905
  return `
2849
2906
  <span class="aparte-tool-label">
2850
2907
  <span class="aparte-tool-icon">${contextConfig().getIcon("tool")}</span>
2851
2908
  <span class="aparte-tool-name">${escapeHtml(name)}</span>
2909
+ <span class="aparte-tool-target"${target ? "" : " hidden"}>${escapeHtml(target)}</span>
2852
2910
  </span>
2853
2911
  <span class="aparte-spinner aparte-tool-spinner" aria-hidden="true"${status === "pending" ? "" : " hidden"}></span>
2854
2912
  <span class="aparte-tool-state">${badge}</span>`;
@@ -2913,6 +2971,7 @@ const toolCallRenderer = {
2913
2971
  }
2914
2972
  const icon = element.querySelector(".aparte-tool-icon");
2915
2973
  if (icon) icon.innerHTML = cfg.getIcon("tool");
2974
+ setTarget(element, segment);
2916
2975
  const statusEl = element.querySelector(".aparte-tool-state");
2917
2976
  if (statusEl) statusEl.innerHTML = stateBadge(segment);
2918
2977
  },
@@ -2953,6 +3012,7 @@ const toolCallRenderer = {
2953
3012
  const badge = element.querySelector(".aparte-tool-state");
2954
3013
  if (badge) badge.innerHTML = stateBadge(segment);
2955
3014
  element.querySelector(".aparte-tool-spinner")?.toggleAttribute("hidden", status !== "pending");
3015
+ setTarget(element, segment);
2956
3016
  setPart(element, "input", input);
2957
3017
  setPart(element, "output", segment.result ?? "");
2958
3018
  highlightParts(element);
@@ -6908,7 +6968,8 @@ export {
6908
6968
  isTerminalStatus as ag,
6909
6969
  adoptMessageSegments as ah,
6910
6970
  APARTE_DERIVED_SEGMENTS as ai,
6911
- rootOf as aj,
6971
+ chatBoundaryOf as aj,
6972
+ rootOf as ak,
6912
6973
  APARTE_DEFAULT_BUBBLE_ACTIONS as b,
6913
6974
  APARTE_DEFAULT_HOST_HANDLERS as c,
6914
6975
  APARTE_DEFAULT_ICON_FALLBACKS as d,
@@ -6935,4 +6996,4 @@ export {
6935
6996
  collectRendererStyles as y,
6936
6997
  contentToText as z
6937
6998
  };
6938
- //# sourceMappingURL=dialog-triggers-Bb-3EkT4.js.map
6999
+ //# sourceMappingURL=dialog-triggers-CJWw0P3l.js.map