dsh-focus-overlay 0.11.1 → 0.11.3

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/lib/client.js +57 -15
  2. package/package.json +2 -1
package/lib/client.js CHANGED
@@ -1035,7 +1035,37 @@ window.__ModuleLoader__.load({
1035
1035
  }
1036
1036
  function InputBar(props) {
1037
1037
  const { t, width, value, queueCount, occCount, errorLine, textareaRef, onFocusChange, onChange, onSend } = props;
1038
- const empty = value.trim() === "";
1038
+ const composingRef = (0, react.useRef)(false);
1039
+ const echoingRef = (0, react.useRef)(false);
1040
+ const [pending, setPending] = (0, react.useState)(null);
1041
+ const shown = pending ?? value;
1042
+ const empty = shown.trim() === "";
1043
+ const echo = (el, next) => {
1044
+ const start = el.selectionStart;
1045
+ const end = el.selectionEnd;
1046
+ echoingRef.current = true;
1047
+ try {
1048
+ onChange(next);
1049
+ } finally {
1050
+ echoingRef.current = false;
1051
+ }
1052
+ if (document.activeElement !== el) {
1053
+ el.focus();
1054
+ el.setSelectionRange(start, end);
1055
+ }
1056
+ };
1057
+ const flush = (el) => {
1058
+ setPending(null);
1059
+ echo(el, el.value);
1060
+ };
1061
+ const onCompositionStart = () => {
1062
+ composingRef.current = true;
1063
+ setPending(value);
1064
+ };
1065
+ const onCompositionEnd = (e) => {
1066
+ composingRef.current = false;
1067
+ flush(e.currentTarget);
1068
+ };
1039
1069
  const onKeyDown = (e) => {
1040
1070
  if (e.key === "Enter" && !e.shiftKey && !e.nativeEvent.isComposing && e.nativeEvent.keyCode !== 229) {
1041
1071
  e.preventDefault();
@@ -1046,15 +1076,18 @@ window.__ModuleLoader__.load({
1046
1076
  el.style.height = "auto";
1047
1077
  el.style.height = `${Math.min(el.scrollHeight, 120)}px`;
1048
1078
  };
1049
- const refocusAfterEcho = (el) => {
1050
- if (document.activeElement === el) return;
1051
- el.focus();
1052
- const end = el.value.length;
1053
- el.setSelectionRange(end, end);
1054
- };
1055
1079
  (0, react.useEffect)(() => {
1056
1080
  if (textareaRef.current) autoGrow(textareaRef.current);
1057
- }, [value, textareaRef]);
1081
+ }, [shown, textareaRef]);
1082
+ const handleInput = (e) => {
1083
+ const el = e.target;
1084
+ if (composingRef.current) setPending(el.value);
1085
+ else {
1086
+ setPending(null);
1087
+ echo(el, el.value);
1088
+ }
1089
+ autoGrow(el);
1090
+ };
1058
1091
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1059
1092
  className: "fm-bar",
1060
1093
  style: { maxWidth: Math.min(width, 680) },
@@ -1074,16 +1107,25 @@ window.__ModuleLoader__.load({
1074
1107
  ref: textareaRef,
1075
1108
  className: "fm-bar-text",
1076
1109
  rows: 1,
1077
- value,
1110
+ value: shown,
1078
1111
  placeholder: t("composer.placeholder"),
1079
- onChange: (e) => {
1080
- onChange(e.target.value);
1081
- autoGrow(e.target);
1082
- refocusAfterEcho(e.target);
1083
- },
1112
+ onChange: handleInput,
1113
+ onCompositionStart,
1114
+ onCompositionEnd,
1084
1115
  onKeyDown,
1085
1116
  onFocus: () => onFocusChange(true),
1086
- onBlur: () => onFocusChange(false)
1117
+ onBlur: () => {
1118
+ if (echoingRef.current) return;
1119
+ if (composingRef.current) {
1120
+ composingRef.current = false;
1121
+ const el = textareaRef.current;
1122
+ if (el) {
1123
+ flush(el);
1124
+ if (document.activeElement === el) return;
1125
+ }
1126
+ }
1127
+ onFocusChange(false);
1128
+ }
1087
1129
  }),
1088
1130
  queueCount > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1089
1131
  className: "fm-bar-queue",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-focus-overlay",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "Focus Mode for the dsh web GUI: a full-screen reading overlay that hides chrome and folds the AI tool-call flow into summaries, reusing the official Markdown / image primitives.",
5
5
  "type": "module",
6
6
  "main": "lib/index.mjs",
@@ -54,6 +54,7 @@
54
54
  "bundle"
55
55
  ],
56
56
  "devDependencies": {
57
+ "playwright-core": "^1.63.0",
57
58
  "tsdown": "0.22.2",
58
59
  "vitest": "^4.1.10"
59
60
  },