@treeseed/dsh-chapters 0.2.13 → 0.2.15

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/lib/client.js CHANGED
@@ -523,6 +523,14 @@ window.__ModuleLoader__.load({
523
523
  if (state.status === "failed") return state.error !== null && state.error !== "" ? `Cannot recommend: ${state.error}` : "Cannot recommend a next step right now";
524
524
  return "No suggestion yet — I will recommend one after your next turn";
525
525
  }
526
+ const copyToClipboard = (text) => {
527
+ try {
528
+ navigator.clipboard?.writeText(text);
529
+ return true;
530
+ } catch {
531
+ return false;
532
+ }
533
+ };
526
534
  const canRunHere = (state) => state.text !== null && state.text !== "";
527
535
  /** How long a state pull may take before we call it failed. Generous on purpose: this is a local RPC that
528
536
  * answers in milliseconds when the session is attachable, and the failure it guards against is a hang. */
@@ -768,27 +776,37 @@ window.__ModuleLoader__.load({
768
776
  } catch {}
769
777
  const refuse = (why) => {
770
778
  console.warn(`[client:prompt] click-to-draft refused: ${why}`);
779
+ const copied = copyToClipboard(state.text ?? "");
771
780
  dispatch({
772
781
  t: "note",
773
- note: "This host does not let a plugin write the composer, so the prompt cannot be edited before sending — Run submits it as your turn"
782
+ note: copied ? "The composer refused this write (" + why.slice(0, 60) + ") — the prompt is COPIED: paste it into the input to edit, or press Run ▶ to send as-is." : "The composer refused this write (" + why.slice(0, 60) + ") — press Run ▶ to send the offer as-is, or copy the text above."
774
783
  });
775
784
  };
785
+ let wrote = false;
776
786
  try {
777
- inputActions.insertText(state.text);
778
- const box = document.querySelector("[contenteditable=\"true\"][role=\"textbox\"]");
779
- const head = state.text.slice(0, 24);
780
- if ((box?.innerText ?? "").indexOf(head) === -1) setTimeout(() => {
781
- if ((document.querySelector("[contenteditable=\"true\"][role=\"textbox\"]")?.innerText ?? "").indexOf(head) === -1) refuse("insertText reported success and left the editor empty");
782
- }, 400);
783
- } catch (error) {
784
- refuse(String(error?.message ?? error).slice(0, 160));
787
+ const span = typeof inputActions.captureInsertion === "function" ? inputActions.captureInsertion() : null;
788
+ if (span !== null && span !== void 0) wrote = inputActions.insertText(state.text, span) === true;
789
+ } catch {
790
+ wrote = false;
791
+ }
792
+ if (!wrote && typeof inputActions.setDraft === "function") try {
793
+ inputActions.setDraft(state.text);
794
+ wrote = true;
795
+ } catch {
796
+ wrote = false;
797
+ }
798
+ if (!wrote) {
799
+ refuse("neither insertText nor setDraft was accepted");
800
+ return;
785
801
  }
802
+ const head = state.text.slice(0, 24);
786
803
  setTimeout(() => {
787
804
  try {
788
- const box = document.querySelector("[contenteditable=\"true\"][role=\"textbox\"]");
789
- console.info(`[client:prompt] after insertText: editorText=${String(box?.innerText?.length ?? "NO-BOX")} focused=${String(document.activeElement === box)}`);
805
+ const dom = (document.querySelector("[contenteditable=\"true\"][role=\"textbox\"]")?.innerText ?? "").includes(head);
806
+ const stored = Object.entries(localStorage).some(([, v]) => String(v).includes(head));
807
+ if (!dom && !stored) refuse("the write was accepted but nothing holds the text");
790
808
  } catch {}
791
- }, 250);
809
+ }, 400);
792
810
  return;
793
811
  }
794
812
  if (typeof inputActions?.setDraft === "function") {