@jskit-ai/assistant-core 0.2.7 → 0.2.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 (214) hide show
  1. package/examples/conversation/package.json +5 -5
  2. package/package.json +18 -7
  3. package/src/client/browser.js +21 -0
  4. package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
  5. package/src/client/conversation/AssistantComposerActions.vue +1 -1
  6. package/src/client/conversation/AssistantConversationElement.vue +14 -52
  7. package/src/client/conversation/AssistantGoalControl.vue +13 -10
  8. package/src/client/conversation/AssistantPromptInput.vue +6 -91
  9. package/src/client/conversation/AssistantTranscript.css +505 -0
  10. package/src/client/conversation/AssistantTranscript.vue +24 -1197
  11. package/src/client/conversation/attachmentFiles.js +158 -0
  12. package/src/client/conversation/attachmentQueueItems.js +121 -0
  13. package/src/client/conversation/attachmentUploadRows.js +265 -0
  14. package/src/client/conversation/conversationDisplay.js +54 -0
  15. package/src/client/conversation/messageDelivery.js +13 -9
  16. package/src/client/conversation/retainedConversation.js +14 -3
  17. package/src/client/conversation/submitText.js +13 -2
  18. package/src/client/conversation/transcriptEntries.js +225 -0
  19. package/src/client/conversation/useAssistantAttachments.js +86 -388
  20. package/src/client/conversation/useAssistantQuestions.js +16 -3
  21. package/src/client/conversation/useAssistantSuggestions.js +16 -2
  22. package/src/client/conversation/useScrollToBottom.js +17 -19
  23. package/src/client/conversation/useTextareaAutoGrow.js +115 -0
  24. package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
  25. package/src/client/lib/assistantApi.js +192 -161
  26. package/src/client/types.js +110 -0
  27. package/src/server/conversation/attachments.js +21 -6
  28. package/src/server/conversation/boundedToolLoop.js +1 -0
  29. package/src/server/conversation/claudeClient.js +71 -20
  30. package/src/server/conversation/claudeCommandRun.js +191 -0
  31. package/src/server/conversation/claudeHistory.js +7 -0
  32. package/src/server/conversation/claudeNativeTurn.js +708 -0
  33. package/src/server/conversation/claudeProcess.js +186 -112
  34. package/src/server/conversation/claudeProcessStart.js +214 -0
  35. package/src/server/conversation/claudePromptDelivery.js +183 -0
  36. package/src/server/conversation/claudeSessionCleanup.js +248 -0
  37. package/src/server/conversation/claudeTools.js +15 -3
  38. package/src/server/conversation/claudeTurn.js +212 -1388
  39. package/src/server/conversation/codexAccountReader.js +168 -0
  40. package/src/server/conversation/codexAuthPreflight.js +5 -4
  41. package/src/server/conversation/codexClient.js +18 -6
  42. package/src/server/conversation/codexConfiguration.js +8 -844
  43. package/src/server/conversation/codexConversationStops.js +377 -0
  44. package/src/server/conversation/codexConversationStore.js +119 -85
  45. package/src/server/conversation/codexConversationTurns.js +378 -0
  46. package/src/server/conversation/codexConversations.js +134 -866
  47. package/src/server/conversation/codexDetachedConversation.js +203 -0
  48. package/src/server/conversation/codexDetachedTurn.js +145 -104
  49. package/src/server/conversation/codexEvents.js +5 -200
  50. package/src/server/conversation/codexGoalCommands.js +14 -4
  51. package/src/server/conversation/codexHelperExecution.js +57 -57
  52. package/src/server/conversation/codexHelperThreadLedger.js +30 -288
  53. package/src/server/conversation/codexHelperThreadRecord.js +304 -0
  54. package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
  55. package/src/server/conversation/codexHelperThreads.js +94 -93
  56. package/src/server/conversation/codexHistoryAdapter.js +13 -8
  57. package/src/server/conversation/codexIsolation.js +672 -0
  58. package/src/server/conversation/codexLiveItems.js +379 -0
  59. package/src/server/conversation/codexMessageCommands.js +68 -286
  60. package/src/server/conversation/codexMessageDelivery.js +46 -37
  61. package/src/server/conversation/codexModelCatalog.js +86 -7
  62. package/src/server/conversation/codexNotificationObserver.js +81 -88
  63. package/src/server/conversation/codexNotificationQueue.js +21 -10
  64. package/src/server/conversation/codexObservationControl.js +105 -89
  65. package/src/server/conversation/codexOutput.js +84 -419
  66. package/src/server/conversation/codexProcess.js +29 -1053
  67. package/src/server/conversation/codexProcessLeader.js +1 -0
  68. package/src/server/conversation/codexPromptDispatch.js +241 -0
  69. package/src/server/conversation/codexProvider.js +147 -1807
  70. package/src/server/conversation/codexProviderAuth.js +348 -0
  71. package/src/server/conversation/codexProviderInvalidation.js +371 -0
  72. package/src/server/conversation/codexProviderInventories.js +437 -0
  73. package/src/server/conversation/codexProviderOwner.js +149 -957
  74. package/src/server/conversation/codexProviderRelease.js +465 -0
  75. package/src/server/conversation/codexProviderSelection.js +26 -15
  76. package/src/server/conversation/codexProviderThreads.js +698 -0
  77. package/src/server/conversation/codexProviderTurns.js +453 -0
  78. package/src/server/conversation/codexReasoning.js +32 -23
  79. package/src/server/conversation/codexRenewalCommands.js +18 -6
  80. package/src/server/conversation/codexRenewalThreads.js +7 -22
  81. package/src/server/conversation/codexResultSettlement.js +70 -88
  82. package/src/server/conversation/codexRunJournal.js +91 -97
  83. package/src/server/conversation/codexRuntime.js +426 -0
  84. package/src/server/conversation/codexRuntimeInventory.js +113 -0
  85. package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
  86. package/src/server/conversation/codexRuntimeStart.js +431 -0
  87. package/src/server/conversation/codexRuntimeStop.js +324 -0
  88. package/src/server/conversation/codexSessionCleanup.js +25 -10
  89. package/src/server/conversation/codexSessionReconciliation.js +21 -7
  90. package/src/server/conversation/codexThreadReadiness.js +49 -52
  91. package/src/server/conversation/codexThreadSnapshot.js +210 -0
  92. package/src/server/conversation/codexThreadStatus.js +48 -62
  93. package/src/server/conversation/codexTurn.js +171 -55
  94. package/src/server/conversation/codexTurnActivity.js +210 -0
  95. package/src/server/conversation/codexTurnControl.js +44 -48
  96. package/src/server/conversation/codexTurnEndings.js +610 -0
  97. package/src/server/conversation/codexTurnRecovery.js +97 -848
  98. package/src/server/conversation/codexTurnState.js +138 -111
  99. package/src/server/conversation/completedResponses.js +227 -0
  100. package/src/server/conversation/configuration.js +2 -2
  101. package/src/server/conversation/continuity.js +43 -6
  102. package/src/server/conversation/conversationDelivery.js +394 -0
  103. package/src/server/conversation/conversationEntries.js +365 -0
  104. package/src/server/conversation/conversationEvents.js +192 -0
  105. package/src/server/conversation/conversationGoals.js +187 -0
  106. package/src/server/conversation/conversationHandles.js +237 -0
  107. package/src/server/conversation/conversationReads.js +206 -0
  108. package/src/server/conversation/conversationRefusals.js +58 -0
  109. package/src/server/conversation/conversationReplacement.js +254 -0
  110. package/src/server/conversation/conversationSending.js +273 -0
  111. package/src/server/conversation/conversationStopping.js +121 -0
  112. package/src/server/conversation/conversationTurnDelivery.js +413 -0
  113. package/src/server/conversation/conversationTurns.js +511 -0
  114. package/src/server/conversation/deliveryTiming.js +25 -3
  115. package/src/server/conversation/fileStorage.js +11 -6
  116. package/src/server/conversation/hookBridge.js +14 -3
  117. package/src/server/conversation/index.js +1 -1
  118. package/src/server/conversation/localExecution.js +82 -625
  119. package/src/server/conversation/nativeCommandCallbacks.js +10 -0
  120. package/src/server/conversation/nativeConversationLifecycle.js +379 -0
  121. package/src/server/conversation/nativeHistoryExport.js +27 -18
  122. package/src/server/conversation/normalize.js +9 -0
  123. package/src/server/conversation/openCodeClient.js +295 -366
  124. package/src/server/conversation/openCodeNativeHistory.js +141 -0
  125. package/src/server/conversation/openCodeNativeSession.js +79 -0
  126. package/src/server/conversation/openCodeProcess.js +109 -53
  127. package/src/server/conversation/openCodeProcessOwner.js +565 -0
  128. package/src/server/conversation/openCodeRuntime.js +111 -1814
  129. package/src/server/conversation/openCodeSessionTurns.js +547 -0
  130. package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
  131. package/src/server/conversation/openCodeTools.js +28 -14
  132. package/src/server/conversation/openCodeTurn.js +80 -74
  133. package/src/server/conversation/openCodeTurnMonitor.js +388 -0
  134. package/src/server/conversation/processIdentity.js +582 -0
  135. package/src/server/conversation/provider.js +21 -6
  136. package/src/server/conversation/providers/api.js +268 -107
  137. package/src/server/conversation/providers/claude.js +29 -6
  138. package/src/server/conversation/providers/claudeDriver.js +325 -250
  139. package/src/server/conversation/providers/codex.js +69 -33
  140. package/src/server/conversation/providers/codexDriver.js +503 -1081
  141. package/src/server/conversation/providers/codexDriverConnection.js +331 -0
  142. package/src/server/conversation/providers/codexDriverTurns.js +464 -0
  143. package/src/server/conversation/providers/opencode.js +30 -4
  144. package/src/server/conversation/providers/opencodeDriver.js +321 -659
  145. package/src/server/conversation/providers/opencodeStandalone.js +475 -0
  146. package/src/server/conversation/providers/scoped.js +64 -19
  147. package/src/server/conversation/runtime.js +171 -2863
  148. package/src/server/conversation/storage.js +91 -50
  149. package/src/server/conversation/streams.js +15 -2
  150. package/src/server/conversation/structuredOutput.js +7 -11
  151. package/src/server/conversation/systemPrompt.js +13 -1
  152. package/src/server/conversation/tools.js +47 -15
  153. package/src/server/conversation/transcript.js +49 -20
  154. package/src/server/hosts/claudeTurn.js +2 -4
  155. package/src/server/hosts/codexConfiguration.js +6 -8
  156. package/src/server/hosts/codexEvents.js +4 -2
  157. package/src/server/hosts/codexProcess.js +1 -1
  158. package/src/server/hosts/codexProvider.js +11 -9
  159. package/src/server/hosts/codexTurn.js +3 -3
  160. package/src/server/hosts/openCodeClient.js +4 -2
  161. package/src/server/index.js +0 -2
  162. package/src/server/lib/aiConnectionClient.js +35 -25
  163. package/src/server/lib/ndjson.js +1 -0
  164. package/src/server/lib/providers/anthropicClient.js +15 -6
  165. package/src/server/lib/providers/common.js +10 -6
  166. package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
  167. package/src/server/lib/serviceToolCatalog.js +29 -283
  168. package/src/server/lib/serviceToolEntries.js +270 -0
  169. package/src/server/types.js +78 -0
  170. package/src/shared/assistantResource.js +0 -23
  171. package/src/shared/conversation/answerChoiceSugar.js +2 -14
  172. package/src/shared/conversation/messageInputSugar.js +18 -0
  173. package/src/shared/conversation/numberedQuestionSugar.js +6 -16
  174. package/src/shared/conversation/pagination.js +1 -1
  175. package/src/shared/conversation/questions.js +1 -0
  176. package/src/shared/conversation/streaming.js +1 -0
  177. package/src/shared/index.js +0 -1
  178. package/src/testing/conversationStorageContract.js +20 -0
  179. package/test/aiConnectionClient.test.js +42 -1
  180. package/test/apiConversationDriver.test.js +217 -38
  181. package/test/assistantBoundedToolLoop.test.js +1 -1
  182. package/test/claudeConversationSession.test.js +15 -11
  183. package/test/claudeInstructionResume.test.js +1 -1
  184. package/test/claudeProcess.test.js +9 -9
  185. package/test/claudeTurn.test.js +21 -8
  186. package/test/codexAccountChange.test.js +5 -3
  187. package/test/codexConversationSession.test.js +22 -21
  188. package/test/codexEvents.test.js +3 -1
  189. package/test/codexHelperDeletion.test.js +3 -7
  190. package/test/codexIsolation.test.js +6 -6
  191. package/test/codexModelCatalog.test.js +6 -7
  192. package/test/codexProcess.test.js +3 -3
  193. package/test/codexThreadSelection.test.js +4 -1
  194. package/test/codexTurn.test.js +29 -20
  195. package/test/conversationApiFacilities.test.js +11 -9
  196. package/test/conversationAttachments.test.js +3 -3
  197. package/test/conversationConfiguration.test.js +4 -4
  198. package/test/conversationNativeStorage.test.js +11 -10
  199. package/test/conversationReplacement.test.js +13 -13
  200. package/test/conversationRuntime.test.js +9 -5
  201. package/test/conversationSession.test.js +78 -22
  202. package/test/conversationTools.test.js +47 -26
  203. package/test/fileConversationStorage.test.js +10 -3
  204. package/test/fixtures/codexHelperThreadLedger.js +3 -3
  205. package/test/frameworkActions.test.js +33 -18
  206. package/test/localConversationExecution.test.js +4 -4
  207. package/test/localExecution.test.js +1 -1
  208. package/test/openCodeConnectionChange.test.js +3 -2
  209. package/test/openCodeRuntime.test.js +8 -7
  210. package/test/openCodeTools.test.js +1 -1
  211. package/test/opencodeConversationSession.test.js +4 -3
  212. package/test/serviceToolCatalog.test.js +3 -8
  213. package/src/server/lib/assistantCompletion.js +0 -190
  214. package/src/server/lib/assistantToolLoop.js +0 -259
@@ -2,7 +2,13 @@ import { computed, ref, toValue, watch } from "vue";
2
2
  import { numberedQuestionSubmissionText, parseNumberedQuestionPrompt } from "../../shared/conversation/numberedQuestionSugar.js";
3
3
  import { parseAnswerChoicePrompt } from "../../shared/conversation/answerChoiceSugar.js";
4
4
 
5
- /** Ordinary chat answer fields; the caller owns authorized message selection and delivery. */
5
+ /**
6
+ * Ordinary chat answer fields; the caller owns authorized message selection and delivery.
7
+ * @param {{
8
+ * message?: import("vue").MaybeRefOrGetter<string>,
9
+ * extraChoice?: import("vue").MaybeRefOrGetter<{ value: string, title?: string } | null>
10
+ * }} [options]
11
+ */
6
12
  function useAssistantQuestions({ message = "", extraChoice = null } = {}) {
7
13
  const questionText = computed(() => String(toValue(message) || ""));
8
14
  const answers = ref({});
@@ -69,8 +75,15 @@ function useAssistantQuestions({ message = "", extraChoice = null } = {}) {
69
75
  choice.value = "";
70
76
  });
71
77
 
72
- return { questions, selectItems, choices, answers, choice, active, capture, markSubmitted, dismiss, reset,
73
- setAnswers(value) { answers.value = value; }, setChoice(value) { choice.value = value; } };
78
+ function setAnswers(value) {
79
+ answers.value = value;
80
+ }
81
+
82
+ function setChoice(value) {
83
+ choice.value = value;
84
+ }
85
+
86
+ return { questions, selectItems, choices, answers, choice, active, capture, markSubmitted, dismiss, reset, setAnswers, setChoice };
74
87
  }
75
88
 
76
89
  export { useAssistantQuestions };
@@ -3,11 +3,24 @@ import { useJskitText } from "../i18n.js";
3
3
 
4
4
  const { serverMessage } = useJskitText();
5
5
 
6
- /** Suggestion requests use an application-supplied agent; credentials stay on its server. */
6
+ /**
7
+ * Suggestion requests use an application-supplied agent; credentials stay on its server.
8
+ * @param {{
9
+ * active?: import("vue").MaybeRefOrGetter<boolean>,
10
+ * requestKey?: import("vue").MaybeRefOrGetter<unknown>,
11
+ * draft?: import("vue").MaybeRefOrGetter<string>,
12
+ * configuration?: import("vue").MaybeRefOrGetter<object>,
13
+ * generate?: import("vue").MaybeRef<((input: { draft: string, configuration: object, signal: AbortSignal }) =>
14
+ * Promise<{ label: string, prompt: string }[]>) | null>,
15
+ * onSelect?: (prompt: string) => boolean | void,
16
+ * debounceMs?: import("vue").MaybeRefOrGetter<number>,
17
+ * minIntervalMs?: import("vue").MaybeRefOrGetter<number>
18
+ * }} options
19
+ */
7
20
  export function useAssistantSuggestions({
8
21
  active = true, requestKey = "", draft = "", configuration = {},
9
22
  generate, onSelect = () => false, debounceMs = 750, minIntervalMs = 0
10
- } = {}) {
23
+ }) {
11
24
  const items = ref([]);
12
25
  const loading = ref(false);
13
26
  const preview = ref("");
@@ -17,6 +30,7 @@ export function useAssistantSuggestions({
17
30
  const currentDraft = computed(() => String(toValue(draft) ?? "").trim());
18
31
  const configurationKey = computed(() => JSON.stringify(toValue(configuration) ?? {}));
19
32
  const key = computed(() => JSON.stringify([toValue(requestKey), currentDraft.value, configurationKey.value]));
33
+ // readable-check: allow method-probe - optional operation: without a generator, or while its ref holds none, no suggestions are requested
20
34
  const eligible = computed(() => toValue(active) !== false && typeof unref(generate) === "function" && dismissedKey.value !== key.value);
21
35
  const visible = computed(() => eligible.value && (loading.value || items.value.length > 0));
22
36
  let timer;
@@ -1,4 +1,5 @@
1
1
  import { nextTick, onBeforeUnmount, unref } from "vue";
2
+ import { hasWindowFunction, waitForLayoutFrame } from "../browser.js";
2
3
 
3
4
  const DEFAULT_SETTLE_DELAYS_MS = [0, 80];
4
5
 
@@ -6,25 +7,12 @@ function readElement(value) {
6
7
  return unref(value) || null;
7
8
  }
8
9
 
9
- function hasWindowTimer(name) {
10
- return typeof window !== "undefined" && typeof window[name] === "function";
11
- }
12
-
13
- function waitForLayoutFrame() {
14
- if (typeof window === "undefined" || typeof window.requestAnimationFrame !== "function") {
15
- return Promise.resolve();
16
- }
17
-
18
- return new Promise((resolve) => {
19
- window.requestAnimationFrame(resolve);
20
- });
21
- }
22
-
23
10
  function normalizedScrollBehavior(value = "") {
24
11
  return value === "smooth" ? "smooth" : "auto";
25
12
  }
26
13
 
27
14
  function scrollElementToBottom(targetElement, behavior = "auto") {
15
+ // readable-check: allow method-probe - platform object: an older browser's elements have no scrollTo()
28
16
  if (behavior === "smooth" && typeof targetElement.scrollTo === "function") {
29
17
  targetElement.scrollTo({
30
18
  behavior,
@@ -35,13 +23,23 @@ function scrollElementToBottom(targetElement, behavior = "auto") {
35
23
  targetElement.scrollTop = targetElement.scrollHeight;
36
24
  }
37
25
 
26
+ /**
27
+ * Scrolls `target` to its end now, after the next layout, and again after each settle delay, while `enabled`.
28
+ * @param {{
29
+ * anchor?: import("vue").MaybeRef<Element | null>,
30
+ * enabled?: import("vue").MaybeRef<boolean>,
31
+ * scrollAnchorIntoView?: boolean,
32
+ * settleDelaysMs?: number[],
33
+ * target: import("vue").MaybeRef<Element | null>
34
+ * }} options
35
+ */
38
36
  function useScrollToBottom({
39
37
  anchor = null,
40
38
  enabled = true,
41
39
  scrollAnchorIntoView = true,
42
40
  settleDelaysMs = DEFAULT_SETTLE_DELAYS_MS,
43
41
  target
44
- } = {}) {
42
+ }) {
45
43
  const scheduledTimers = new Set();
46
44
  let disposed = false;
47
45
  let scheduleVersion = 0;
@@ -51,7 +49,7 @@ function useScrollToBottom({
51
49
  }
52
50
 
53
51
  function clearScheduledTimers() {
54
- if (hasWindowTimer("clearTimeout")) {
52
+ if (hasWindowFunction("clearTimeout")) {
55
53
  scheduledTimers.forEach((timer) => {
56
54
  window.clearTimeout(timer);
57
55
  });
@@ -64,7 +62,7 @@ function useScrollToBottom({
64
62
  clearScheduledTimers();
65
63
  }
66
64
 
67
- function scrollNow(options = {}) {
65
+ function scrollNow(options) {
68
66
  if (disposed || !isEnabled()) {
69
67
  return;
70
68
  }
@@ -83,7 +81,7 @@ function useScrollToBottom({
83
81
  if (behavior === "smooth") {
84
82
  anchorOptions.behavior = behavior;
85
83
  }
86
- readElement(anchor)?.scrollIntoView?.(anchorOptions);
84
+ readElement(anchor)?.scrollIntoView(anchorOptions);
87
85
  }
88
86
  scrollElementToBottom(targetElement, behavior);
89
87
  }
@@ -93,7 +91,7 @@ function useScrollToBottom({
93
91
  disposed ||
94
92
  version !== scheduleVersion ||
95
93
  !isEnabled() ||
96
- !hasWindowTimer("setTimeout")
94
+ !hasWindowFunction("setTimeout")
97
95
  ) {
98
96
  return;
99
97
  }
@@ -0,0 +1,115 @@
1
+ import { onBeforeUnmount, onMounted, watch } from "vue";
2
+
3
+ /**
4
+ * Grows the prompt's textarea with its text, up to its CSS max-height, after each change of its model value,
5
+ * placeholder, rows or density and each change of its width. preserveHeightForNextModelValue() keeps the current
6
+ * height through the next model value change; typing ends that hold.
7
+ * @param {{
8
+ * autoGrow: boolean,
9
+ * density: string,
10
+ * modelValue: string,
11
+ * placeholder: string,
12
+ * placeholderAffectsHeight: boolean,
13
+ * rows: number | string
14
+ * }} props The prompt input's props.
15
+ * @param {import("vue").Ref<HTMLTextAreaElement | null>} textareaRef
16
+ */
17
+ function useTextareaAutoGrow(props, textareaRef) {
18
+ let resizeFrame = 0;
19
+ let resizeObserver = null;
20
+ let preserveHeightForNextModelValueChange = false;
21
+
22
+ function resizeTextarea() {
23
+ if (!props.autoGrow) {
24
+ return;
25
+ }
26
+ const textarea = textareaRef.value;
27
+ if (!textarea) {
28
+ return;
29
+ }
30
+ if (!props.placeholderAffectsHeight && !textarea.value) {
31
+ return;
32
+ }
33
+ const style = window.getComputedStyle(textarea);
34
+ const minHeight = Number.parseFloat(style.minHeight) || 0;
35
+ const maxHeight = Number.parseFloat(style.maxHeight) || Number.POSITIVE_INFINITY;
36
+ textarea.style.height = "auto";
37
+ const contentHeight = Math.max(textarea.scrollHeight, minHeight);
38
+ const targetHeight = Math.min(contentHeight, maxHeight);
39
+ textarea.style.height = `${targetHeight}px`;
40
+ textarea.style.overflowY = contentHeight > targetHeight + 1 ? "auto" : "hidden";
41
+ }
42
+
43
+ function queueResizeTextarea() {
44
+ if (!props.autoGrow || typeof window === "undefined") {
45
+ return;
46
+ }
47
+ if (resizeFrame) return;
48
+ resizeFrame = window.requestAnimationFrame(() => {
49
+ resizeFrame = 0;
50
+ resizeTextarea();
51
+ });
52
+ }
53
+
54
+ function preserveHeightForNextModelValue() {
55
+ const textarea = textareaRef.value;
56
+ if (!props.autoGrow || !textarea) {
57
+ return false;
58
+ }
59
+ if (resizeFrame) {
60
+ window.cancelAnimationFrame(resizeFrame);
61
+ resizeFrame = 0;
62
+ }
63
+ preserveHeightForNextModelValueChange = true;
64
+ textarea.style.height = `${textarea.getBoundingClientRect().height}px`;
65
+ textarea.style.overflowY = "auto";
66
+ return true;
67
+ }
68
+
69
+ /** Typing writes the model value itself, so its next change resizes the textarea again. */
70
+ function releasePreservedHeight() {
71
+ preserveHeightForNextModelValueChange = false;
72
+ }
73
+
74
+ onMounted(() => {
75
+ queueResizeTextarea();
76
+ let lastWidth = 0;
77
+ resizeObserver = new ResizeObserver(([entry]) => {
78
+ // Height changes are our own output. Only width changes need another layout.
79
+ const width = entry.contentRect.width;
80
+ if (width !== lastWidth) {
81
+ lastWidth = width;
82
+ queueResizeTextarea();
83
+ }
84
+ });
85
+ resizeObserver.observe(textareaRef.value);
86
+ });
87
+
88
+ onBeforeUnmount(() => {
89
+ resizeObserver?.disconnect();
90
+ if (resizeFrame && typeof window !== "undefined") {
91
+ window.cancelAnimationFrame(resizeFrame);
92
+ resizeFrame = 0;
93
+ }
94
+ });
95
+
96
+ watch(() => [
97
+ props.autoGrow,
98
+ props.modelValue,
99
+ props.placeholder,
100
+ props.placeholderAffectsHeight,
101
+ props.rows,
102
+ props.density
103
+ ], (values, previousValues = []) => {
104
+ const modelValueChanged = values[1] !== previousValues[1];
105
+ if (preserveHeightForNextModelValueChange && modelValueChanged) {
106
+ preserveHeightForNextModelValueChange = false;
107
+ return;
108
+ }
109
+ queueResizeTextarea();
110
+ }, { flush: "post" });
111
+
112
+ return { preserveHeightForNextModelValue, queueResizeTextarea, releasePreservedHeight };
113
+ }
114
+
115
+ export { useTextareaAutoGrow };