@hydra-acp/browser 0.1.8 → 0.1.9

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.
Files changed (2) hide show
  1. package/dist/ui/index.html +103 -17
  2. package/package.json +1 -1
@@ -10517,6 +10517,25 @@ pre code { background: transparent; padding: 0; }
10517
10517
  if (!state.current) return;
10518
10518
  state.current.log.push(item);
10519
10519
  }
10520
+ function isWaitingQueuedBubble(item) {
10521
+ if (item.kind !== "stream" || item.role !== "user" || !item.queueEntry) {
10522
+ return false;
10523
+ }
10524
+ const s = item.queueEntry.status;
10525
+ return s === "queued" || s === "editing";
10526
+ }
10527
+ function queuedBoundary() {
10528
+ if (!state.current) return 0;
10529
+ const log = state.current.log;
10530
+ for (let i = 0; i < log.length; i++) {
10531
+ if (isWaitingQueuedBubble(log[i])) return i;
10532
+ }
10533
+ return log.length;
10534
+ }
10535
+ function insertAboveQueued(item) {
10536
+ if (!state.current) return;
10537
+ state.current.log.splice(queuedBoundary(), 0, item);
10538
+ }
10520
10539
  function markActive() {
10521
10540
  if (!state.current) return;
10522
10541
  state.current.inTurn = true;
@@ -10537,9 +10556,11 @@ pre code { background: transparent; padding: 0; }
10537
10556
  if (!state.current) return;
10538
10557
  const text = contentToText(content);
10539
10558
  if (!text) return;
10559
+ const log = state.current.log;
10560
+ const boundary = queuedBoundary();
10540
10561
  let last;
10541
- for (let i = state.current.log.length - 1; i >= 0; i--) {
10542
- const e = state.current.log[i];
10562
+ for (let i = boundary - 1; i >= 0; i--) {
10563
+ const e = log[i];
10543
10564
  if (e.kind !== "spinner") {
10544
10565
  last = e;
10545
10566
  break;
@@ -10549,13 +10570,13 @@ pre code { background: transparent; padding: 0; }
10549
10570
  last.text += text;
10550
10571
  return;
10551
10572
  }
10552
- state.current.log.push({ kind: "stream", role, text });
10573
+ log.splice(boundary, 0, { kind: "stream", role, text });
10553
10574
  }
10554
10575
  function closeOpenStream() {
10555
10576
  if (!state.current) return;
10556
10577
  for (let i = state.current.log.length - 1; i >= 0; i--) {
10557
10578
  const e = state.current.log[i];
10558
- if (e.kind === "stream") {
10579
+ if (e.kind === "stream" && !e.closed) {
10559
10580
  e.closed = true;
10560
10581
  return;
10561
10582
  }
@@ -10596,12 +10617,12 @@ pre code { background: transparent; padding: 0; }
10596
10617
  const c2 = state.current;
10597
10618
  if (c2.spinner) {
10598
10619
  if (!c2.log.some((e) => e.kind === "spinner")) {
10599
- c2.log.push({ kind: "spinner", spinner: c2.spinner });
10620
+ insertAboveQueued({ kind: "spinner", spinner: c2.spinner });
10600
10621
  }
10601
10622
  return;
10602
10623
  }
10603
10624
  c2.spinner = { toolCallIds: [], expanded: false };
10604
- c2.log.push({ kind: "spinner", spinner: c2.spinner });
10625
+ insertAboveQueued({ kind: "spinner", spinner: c2.spinner });
10605
10626
  }
10606
10627
  function onToolCall(update) {
10607
10628
  if (!state.current) return;
@@ -10636,6 +10657,11 @@ pre code { background: transparent; padding: 0; }
10636
10657
  state.current.spinner = null;
10637
10658
  state.current.log = state.current.log.filter((e) => e.kind !== "spinner");
10638
10659
  closeOpenStream();
10660
+ for (const entry of state.current.promptQueue) {
10661
+ if (entry.status === "processing") {
10662
+ entry.status = "done";
10663
+ }
10664
+ }
10639
10665
  state.current.currentPlanEntry = null;
10640
10666
  markIdleAndDrain();
10641
10667
  }
@@ -10652,7 +10678,7 @@ pre code { background: transparent; padding: 0; }
10652
10678
  return;
10653
10679
  }
10654
10680
  closeOpenStream();
10655
- state.current.log.push({
10681
+ insertAboveQueued({
10656
10682
  kind: "stream",
10657
10683
  role: "user",
10658
10684
  text,
@@ -10713,6 +10739,7 @@ pre code { background: transparent; padding: 0; }
10713
10739
  unbound.messageId = messageId;
10714
10740
  state.current.queueByMessageId.set(messageId, unbound);
10715
10741
  const position2 = typeof params.position === "number" ? params.position : 1;
10742
+ unbound.aheadAtEnqueue = Math.max(0, position2);
10716
10743
  if (position2 === 0) {
10717
10744
  unbound.status = "processing";
10718
10745
  } else if (unbound.status === "pending") {
@@ -10744,13 +10771,18 @@ pre code { background: transparent; padding: 0; }
10744
10771
  };
10745
10772
  state.current.queueByMessageId.set(messageId, entry);
10746
10773
  closeOpenStream();
10747
- state.current.log.push({
10774
+ const peerBubble = {
10748
10775
  kind: "stream",
10749
10776
  role: "user",
10750
10777
  text,
10751
10778
  closed: true,
10752
10779
  queueEntry: entry
10753
- });
10780
+ };
10781
+ if (position === 0) {
10782
+ insertAboveQueued(peerBubble);
10783
+ } else {
10784
+ state.current.log.push(peerBubble);
10785
+ }
10754
10786
  }
10755
10787
  function onPromptQueueUpdated(params) {
10756
10788
  if (!state.current) return;
@@ -10800,7 +10832,7 @@ pre code { background: transparent; padding: 0; }
10800
10832
  return;
10801
10833
  }
10802
10834
  const item = { kind: "plan", entries };
10803
- state.current.log.push(item);
10835
+ insertAboveQueued(item);
10804
10836
  state.current.currentPlanEntry = item;
10805
10837
  }
10806
10838
  function onPermissionResolved(update) {
@@ -10986,7 +11018,11 @@ pre code { background: transparent; padding: 0; }
10986
11018
  render();
10987
11019
  return;
10988
11020
  }
10989
- const ahead = c2.promptQueue.length + (c2.inTurn ? 1 : 0);
11021
+ const ownActive = c2.promptQueue.filter(
11022
+ (e) => e.status === "queued" || e.status === "pending" || e.status === "processing"
11023
+ ).length;
11024
+ const peerInFlight = c2.inTurn && !c2.promptQueue.some((e) => e.status === "processing");
11025
+ const ahead = ownActive + (peerInFlight ? 1 : 0);
10990
11026
  const entry = {
10991
11027
  id: "p_" + Math.random().toString(36).slice(2, 10),
10992
11028
  text,
@@ -11016,9 +11052,21 @@ pre code { background: transparent; padding: 0; }
11016
11052
  c2.ownPromptIds.add(String(promptId));
11017
11053
  }
11018
11054
  ensureSpinner();
11055
+ pushHistory(c2, text);
11019
11056
  c2.composerValue = "";
11020
11057
  render();
11021
11058
  }
11059
+ var HISTORY_MAX = 100;
11060
+ function pushHistory(c2, text) {
11061
+ if (c2.history[0] !== text) {
11062
+ c2.history.unshift(text);
11063
+ if (c2.history.length > HISTORY_MAX) {
11064
+ c2.history.length = HISTORY_MAX;
11065
+ }
11066
+ }
11067
+ c2.historyIndex = null;
11068
+ c2.historyDraft = null;
11069
+ }
11022
11070
  function cancelQueuedPrompt(entry) {
11023
11071
  const c2 = state.current;
11024
11072
  if (!c2) return;
@@ -11263,6 +11311,9 @@ pre code { background: transparent; padding: 0; }
11263
11311
  composerValue: "",
11264
11312
  busy: false,
11265
11313
  recentOwnPrompts: [],
11314
+ history: [],
11315
+ historyIndex: null,
11316
+ historyDraft: null,
11266
11317
  _lastMetaFp: session ? `${session.title}|${session.cwd}|${session.agentId}` : "",
11267
11318
  promptQueue: [],
11268
11319
  queueByMessageId: /* @__PURE__ */ new Map(),
@@ -11932,21 +11983,52 @@ Replace it? (Any live attach will be closed.)`
11932
11983
  for (const item of c2.log) {
11933
11984
  body.appendChild(renderLogItem(item));
11934
11985
  }
11986
+ const autosize = (t) => {
11987
+ t.style.height = "auto";
11988
+ t.style.height = t.scrollHeight + "px";
11989
+ };
11990
+ const setComposer = (t, text) => {
11991
+ t.value = text;
11992
+ c2.composerValue = text;
11993
+ autosize(t);
11994
+ t.setSelectionRange(text.length, text.length);
11995
+ };
11935
11996
  const composerOnKey = (e) => {
11936
- if (e.key !== "Enter" || e.isComposing) return;
11997
+ if (e.isComposing) return;
11998
+ const t = e.target;
11999
+ if (e.key === "ArrowUp" && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && t.selectionStart === 0 && t.selectionEnd === 0 && c2.history.length > 0) {
12000
+ const nextIdx = c2.historyIndex === null ? 0 : Math.min(c2.history.length - 1, c2.historyIndex + 1);
12001
+ if (c2.historyIndex === null) {
12002
+ c2.historyDraft = c2.composerValue;
12003
+ }
12004
+ c2.historyIndex = nextIdx;
12005
+ e.preventDefault();
12006
+ setComposer(t, c2.history[nextIdx]);
12007
+ return;
12008
+ }
12009
+ if (e.key === "ArrowDown" && !e.shiftKey && !e.altKey && !e.ctrlKey && !e.metaKey && t.selectionStart === t.value.length && t.selectionEnd === t.value.length && c2.historyIndex !== null) {
12010
+ e.preventDefault();
12011
+ if (c2.historyIndex > 0) {
12012
+ c2.historyIndex -= 1;
12013
+ setComposer(t, c2.history[c2.historyIndex]);
12014
+ } else {
12015
+ const draft = c2.historyDraft ?? "";
12016
+ c2.historyIndex = null;
12017
+ c2.historyDraft = null;
12018
+ setComposer(t, draft);
12019
+ }
12020
+ return;
12021
+ }
12022
+ if (e.key !== "Enter") return;
11937
12023
  if (e.shiftKey) return;
11938
12024
  if (e.altKey || e.ctrlKey || e.metaKey) {
11939
12025
  e.preventDefault();
11940
- insertAtCaret(e.target, "\n");
12026
+ insertAtCaret(t, "\n");
11941
12027
  return;
11942
12028
  }
11943
12029
  e.preventDefault();
11944
12030
  sendPrompt();
11945
12031
  };
11946
- const autosize = (t) => {
11947
- t.style.height = "auto";
11948
- t.style.height = t.scrollHeight + "px";
11949
- };
11950
12032
  const textarea = el(
11951
12033
  "textarea",
11952
12034
  {
@@ -11957,6 +12039,10 @@ Replace it? (Any live attach will be closed.)`
11957
12039
  oninput: (e) => {
11958
12040
  const t = e.target;
11959
12041
  c2.composerValue = t.value;
12042
+ if (c2.historyIndex !== null) {
12043
+ c2.historyIndex = null;
12044
+ c2.historyDraft = null;
12045
+ }
11960
12046
  autosize(t);
11961
12047
  }
11962
12048
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hydra-acp/browser",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Browser-based UI for hydra-acp sessions.",
5
5
  "license": "MIT",
6
6
  "type": "module",