@longline/aqua-ui 1.0.331 → 1.0.335

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 (44) hide show
  1. package/hooks/useSpeechAIRecorder/index.d.ts +1 -0
  2. package/hooks/useSpeechAIRecorder/index.js +1 -0
  3. package/hooks/useSpeechAIRecorder/stories/SpeechAIAudioInput.d.ts +6 -0
  4. package/hooks/{useAssemblyAIRecorder/stories/AssemblyAudioInput.js → useSpeechAIRecorder/stories/SpeechAIAudioInput.js} +4 -4
  5. package/hooks/useSpeechAIRecorder/useSpeechAIRecorder.d.ts +29 -0
  6. package/hooks/{useAssemblyAIRecorder/useAssemblyAIRecorder.js → useSpeechAIRecorder/useSpeechAIRecorder.js} +13 -11
  7. package/hooks/useTextAIStream/index.d.ts +1 -0
  8. package/hooks/useTextAIStream/index.js +1 -0
  9. package/hooks/{useOpenAIStream/stories/OpenAIStreamInput.d.ts → useTextAIStream/stories/TextAIStreamInput.d.ts} +3 -3
  10. package/hooks/{useOpenAIStream/stories/OpenAIStreamInput.js → useTextAIStream/stories/TextAIStreamInput.js} +6 -6
  11. package/hooks/useTextAIStream/useTextAIStream.d.ts +26 -0
  12. package/hooks/{useOpenAIStream/useOpenAIStream.js → useTextAIStream/useTextAIStream.js} +16 -4
  13. package/inputs/DateInput/Selector.js +17 -10
  14. package/inputs/Editor/Editor.d.ts +7 -6
  15. package/inputs/Editor/Editor.js +1 -1
  16. package/inputs/Editor/buttons/{SpeechButton.d.ts → SpeechAIButton.d.ts} +2 -2
  17. package/inputs/Editor/buttons/{SpeechButton.js → SpeechAIButton.js} +5 -5
  18. package/inputs/Editor/buttons/TextAIButton.d.ts +28 -0
  19. package/inputs/Editor/buttons/{OpenAIButton.js → TextAIButton.js} +13 -14
  20. package/inputs/Editor/buttons/{OpenAIMenu.d.ts → TextAIMenu.d.ts} +2 -2
  21. package/inputs/Editor/buttons/{OpenAIMenu.js → TextAIMenu.js} +3 -3
  22. package/inputs/Editor/menu/MenuBar.d.ts +6 -6
  23. package/inputs/Editor/menu/MenuBar.js +7 -7
  24. package/package.json +4 -5
  25. package/hooks/useAssemblyAIRecorder/index.d.ts +0 -1
  26. package/hooks/useAssemblyAIRecorder/index.js +0 -1
  27. package/hooks/useAssemblyAIRecorder/stories/AssemblyAudioInput.d.ts +0 -6
  28. package/hooks/useAssemblyAIRecorder/useAssemblyAIRecorder.d.ts +0 -27
  29. package/hooks/useOpenAIRecorder/index.d.ts +0 -1
  30. package/hooks/useOpenAIRecorder/index.js +0 -1
  31. package/hooks/useOpenAIRecorder/old/AudioInputNoHook.d.ts +0 -8
  32. package/hooks/useOpenAIRecorder/old/AudioInputNoHook.js +0 -192
  33. package/hooks/useOpenAIRecorder/old/AudioInputStandardMedia.d.ts +0 -5
  34. package/hooks/useOpenAIRecorder/old/AudioInputStandardMedia.js +0 -170
  35. package/hooks/useOpenAIRecorder/stories/OpenAIAudioInput.d.ts +0 -8
  36. package/hooks/useOpenAIRecorder/stories/OpenAIAudioInput.js +0 -17
  37. package/hooks/useOpenAIRecorder/useOpenAIRecorder.d.ts +0 -22
  38. package/hooks/useOpenAIRecorder/useOpenAIRecorder.js +0 -223
  39. package/hooks/useOpenAIStream/index.d.ts +0 -1
  40. package/hooks/useOpenAIStream/index.js +0 -1
  41. package/hooks/useOpenAIStream/useOpenAIStream.d.ts +0 -14
  42. package/inputs/Editor/buttons/OpenAIButton.d.ts +0 -29
  43. /package/hooks/{useAssemblyAIRecorder → useSpeechAIRecorder}/SpeechManager.d.ts +0 -0
  44. /package/hooks/{useAssemblyAIRecorder → useSpeechAIRecorder}/SpeechManager.js +0 -0
@@ -0,0 +1 @@
1
+ export { useSpeechAIRecorder } from './useSpeechAIRecorder';
@@ -0,0 +1 @@
1
+ export { useSpeechAIRecorder } from './useSpeechAIRecorder';
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface IProps {
3
+ url: string;
4
+ }
5
+ declare const SpeechAIAudioInput: (props: IProps) => React.JSX.Element;
6
+ export { SpeechAIAudioInput };
@@ -1,8 +1,8 @@
1
1
  import React, { useState } from 'react';
2
- import { useAssemblyAIRecorder } from '../useAssemblyAIRecorder';
3
- var AssemblyAudioInput = function (props) {
2
+ import { useSpeechAIRecorder } from '../useSpeechAIRecorder';
3
+ var SpeechAIAudioInput = function (props) {
4
4
  var _a = useState('initial'), transcript = _a[0], setTranscript = _a[1];
5
- var _b = useAssemblyAIRecorder(props.url, setTranscript), recordingStatus = _b.recordingStatus, toggleRecording = _b.toggleRecording;
5
+ var _b = useSpeechAIRecorder(props.url, setTranscript), recordingStatus = _b.recordingStatus, toggleRecording = _b.toggleRecording;
6
6
  var getLabel = function (recordingStatus) {
7
7
  switch (recordingStatus) {
8
8
  case 'connecting': return "Connecting";
@@ -14,4 +14,4 @@ var AssemblyAudioInput = function (props) {
14
14
  React.createElement("button", { onClick: toggleRecording }, getLabel(recordingStatus)),
15
15
  React.createElement("p", { style: { border: 'solid 1px blue', minHeight: '200px', color: 'white' } }, transcript)));
16
16
  };
17
- export { AssemblyAudioInput };
17
+ export { SpeechAIAudioInput };
@@ -0,0 +1,29 @@
1
+ type TranscriptCallback = (text: string) => void;
2
+ type TRecordingStatus = 'idle' | 'connecting' | 'recording';
3
+ /**
4
+ * useSpeechAIRecorder
5
+ *
6
+ * A custom React hook for real-time audio transcription. The current
7
+ * implementation streams audio over a WebSocket to AssemblyAI's v3 streaming
8
+ * endpoint, having first obtained a temporary token from the supplied
9
+ * back-end URL. Switching providers would mean rewriting this hook's
10
+ * networking — the public interface is provider-agnostic.
11
+ *
12
+ * When recording is toggled on, it takes a few moments to establish a
13
+ * WebSocket connection. During this time, the recording status will be
14
+ * `'connecting'`.
15
+ *
16
+ * @param url - Back-end URL for obtaining a temporary streaming token.
17
+ * @param onTranscript - Callback that receives updated transcript text.
18
+ * @returns An object containing:
19
+ * - recordingStatus: 'idle' | 'connecting' | 'recording'
20
+ * - toggleRecording: function to start or stop recording
21
+ *
22
+ * The `SpeechManager` singleton tracks the active recorder; when a new
23
+ * recording is started, the previous one is stopped.
24
+ */
25
+ declare const useSpeechAIRecorder: (url: string, onTranscript: TranscriptCallback, authToken?: string) => {
26
+ recordingStatus: TRecordingStatus;
27
+ toggleRecording: () => Promise<void>;
28
+ };
29
+ export { useSpeechAIRecorder, TRecordingStatus };
@@ -46,26 +46,28 @@ import { SpeechManager } from './SpeechManager';
46
46
  //
47
47
  var moduleScript = /* js */ "\nconst MAX_16BIT_INT = 32767\nclass AudioProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n\n // Input is browser sample rate (typically 48000 Hz in Firefox)\n // This variable is globally available inside the AudioWorkletProcessor\n // scope.\n this.inputSampleRate = sampleRate;\n console.log('Detected input sample rate:', sampleRate);\n this.targetSampleRate = 16000;\n this.ratio = this.inputSampleRate / this.targetSampleRate;\n\n // Buffer of input samples for resampling\n this.buffer = [];\n\n // Number of samples per output chunk\n this.chunkSize = 1600; // ~100ms at 16kHz\n }\n\n /**\n * Resample the input buffer to the target sample rate using linear interpolation.\n */\n resample(inputBuffer) {\n const outputLength = Math.floor(inputBuffer.length / this.ratio);\n const output = new Float32Array(outputLength);\n\n for (let i = 0; i < outputLength; i++) {\n const idx = i * this.ratio;\n const idxInt = Math.floor(idx);\n const idxFrac = idx - idxInt;\n\n const sample1 = inputBuffer[idxInt] || 0;\n const sample2 = inputBuffer[idxInt + 1] || 0;\n output[i] = sample1 + (sample2 - sample1) * idxFrac;\n }\n\n return output;\n }\n\n /**\n * Converts Float32 PCM [-1, 1] to 16-bit signed PCM\n */\n floatTo16BitPCM(float32Array) {\n const int16Array = new Int16Array(float32Array.length);\n for (let i = 0; i < float32Array.length; i++) {\n let s = Math.max(-1, Math.min(1, float32Array[i]));\n int16Array[i] = s < 0 ? s * 0x8000 : s * 0x7FFF;\n }\n return int16Array;\n }\n\n /**\n * Called automatically by browser on each audio frame (128 samples per channel).\n */\n process(inputs, outputs, parameters) {\n try {\n const input = inputs[0];\n if (!input || input.length === 0) return true;\n\n // Read from mono channel\n const channelData = input[0];\n this.buffer.push(...channelData);\n\n // Only process if we have enough input to produce a full output chunk\n const neededInputSamples = this.chunkSize * this.ratio;\n if (this.buffer.length >= neededInputSamples) {\n const inputForChunk = this.buffer.slice(0, neededInputSamples);\n const resampled = this.resample(inputForChunk);\n const pcm = this.floatTo16BitPCM(resampled);\n\n this.port.postMessage({ audio_data: pcm }); // Send Int16Array to main thread\n\n // Remove consumed input samples\n this.buffer = this.buffer.slice(neededInputSamples);\n }\n\n return true; // Keep processor alive\n } catch (error) {\n console.error(error)\n return false\n }\n }\n}\n\nregisterProcessor('audio-processor', AudioProcessor);\n";
48
48
  /**
49
- * useAssemblyAIRecorder
49
+ * useSpeechAIRecorder
50
50
  *
51
- * A custom React hook for real-time audio transcription using AssemblyAI's
52
- * streaming API.
51
+ * A custom React hook for real-time audio transcription. The current
52
+ * implementation streams audio over a WebSocket to AssemblyAI's v3 streaming
53
+ * endpoint, having first obtained a temporary token from the supplied
54
+ * back-end URL. Switching providers would mean rewriting this hook's
55
+ * networking — the public interface is provider-agnostic.
53
56
  *
54
- * When recording is toggled on, it takes a few moments to establish a web
55
- * socket connection. During this time, the recording status will be
57
+ * When recording is toggled on, it takes a few moments to establish a
58
+ * WebSocket connection. During this time, the recording status will be
56
59
  * `'connecting'`.
57
60
  *
58
- * @param url - Back-end URL for obtaining temporary token
61
+ * @param url - Back-end URL for obtaining a temporary streaming token.
59
62
  * @param onTranscript - Callback that receives updated transcript text.
60
63
  * @returns An object containing:
61
64
  * - recordingStatus: 'idle' | 'connecting' | 'recording'
62
65
  * - toggleRecording: function to start or stop recording
63
66
  *
64
- * The `SpeechManager` singleton is use to keep track of which
65
- * AssemblyAIRecorder is currently recording; when a new recording is started,
66
- * the current one is stopped.
67
+ * The `SpeechManager` singleton tracks the active recorder; when a new
68
+ * recording is started, the previous one is stopped.
67
69
  */
68
- var useAssemblyAIRecorder = function (url, onTranscript, authToken) {
70
+ var useSpeechAIRecorder = function (url, onTranscript, authToken) {
69
71
  // State to track the current recording status
70
72
  var _a = useState('idle'), recordingStatus = _a[0], setRecordingStatus = _a[1];
71
73
  // WebSocket connection to AssemblyAI
@@ -294,4 +296,4 @@ var useAssemblyAIRecorder = function (url, onTranscript, authToken) {
294
296
  toggleRecording: toggleRecording,
295
297
  };
296
298
  };
297
- export { useAssemblyAIRecorder };
299
+ export { useSpeechAIRecorder };
@@ -0,0 +1 @@
1
+ export { useTextAIStream } from './useTextAIStream';
@@ -0,0 +1 @@
1
+ export { useTextAIStream } from './useTextAIStream';
@@ -5,7 +5,7 @@ interface IProps {
5
5
  */
6
6
  url: string;
7
7
  /**
8
- * Prompt to send to OpenAI.
8
+ * Prompt to send.
9
9
  */
10
10
  prompt: string;
11
11
  /**
@@ -19,5 +19,5 @@ interface IProps {
19
19
  */
20
20
  top_p?: number;
21
21
  }
22
- declare const OpenAIStreamInput: (props: IProps) => React.JSX.Element;
23
- export { OpenAIStreamInput };
22
+ declare const TextAIStreamInput: (props: IProps) => React.JSX.Element;
23
+ export { TextAIStreamInput };
@@ -35,16 +35,16 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
35
35
  }
36
36
  };
37
37
  import React, { useState } from 'react';
38
- import { useOpenAIStream } from '../useOpenAIStream';
39
- var OpenAIStreamInput = function (props) {
40
- var streamOpenAI = useOpenAIStream(props.url).stream;
38
+ import { useTextAIStream } from '../useTextAIStream';
39
+ var TextAIStreamInput = function (props) {
40
+ var stream = useTextAIStream(props.url).stream;
41
41
  var _a = useState(""), result = _a[0], setResult = _a[1];
42
42
  var handleClick = function () { return __awaiter(void 0, void 0, void 0, function () {
43
43
  return __generator(this, function (_a) {
44
44
  switch (_a.label) {
45
45
  case 0:
46
46
  setResult("");
47
- return [4 /*yield*/, streamOpenAI(props.prompt, function (chunk) { return __awaiter(void 0, void 0, void 0, function () {
47
+ return [4 /*yield*/, stream(props.prompt, function (chunk) { return __awaiter(void 0, void 0, void 0, function () {
48
48
  return __generator(this, function (_a) {
49
49
  setResult(function (prev) { return prev + chunk; });
50
50
  return [2 /*return*/];
@@ -60,7 +60,7 @@ var OpenAIStreamInput = function (props) {
60
60
  React.createElement("div", null,
61
61
  "Prompt: ",
62
62
  props.prompt),
63
- React.createElement("button", { onClick: handleClick }, "Call OpenAI"),
63
+ React.createElement("button", { onClick: handleClick }, "Stream"),
64
64
  React.createElement("p", { style: { border: 'solid 1px blue', minHeight: '200px', color: 'white' } }, result)));
65
65
  };
66
- export { OpenAIStreamInput };
66
+ export { TextAIStreamInput };
@@ -0,0 +1,26 @@
1
+ interface ITextAIStreamOptions {
2
+ temperature: number;
3
+ top_p: number;
4
+ }
5
+ /**
6
+ * Hook to stream text from a backend AI endpoint.
7
+ *
8
+ * The backend is expected to relay/translate its provider's output into
9
+ * OpenAI chat-completions Server-Sent Events:
10
+ *
11
+ * data: {"choices":[{"delta":{"content":"..."}}]}\n\n
12
+ * ...
13
+ * data: [DONE]\n\n
14
+ *
15
+ * This is the de-facto streaming format and is provider-agnostic in practice
16
+ * (many vendors expose OpenAI-compatible endpoints). Backends that wrap other
17
+ * providers (e.g. Anthropic) should translate their native SSE into this
18
+ * shape so the hook stays unchanged.
19
+ *
20
+ * @returns `stream`: a function that takes a prompt and streaming callback.
21
+ */
22
+ declare const useTextAIStream: (url: string, authToken?: string) => {
23
+ stream: (prompt: string, onText: (text: string) => Promise<void>, options?: ITextAIStreamOptions) => Promise<void>;
24
+ cancel: () => void;
25
+ };
26
+ export { useTextAIStream, ITextAIStreamOptions };
@@ -45,11 +45,23 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
45
45
  };
46
46
  import { useCallback, useRef } from 'react';
47
47
  /**
48
- * Hook to stream OpenAI responses via a backend endpoint that acts as a proxy.
48
+ * Hook to stream text from a backend AI endpoint.
49
+ *
50
+ * The backend is expected to relay/translate its provider's output into
51
+ * OpenAI chat-completions Server-Sent Events:
52
+ *
53
+ * data: {"choices":[{"delta":{"content":"..."}}]}\n\n
54
+ * ...
55
+ * data: [DONE]\n\n
56
+ *
57
+ * This is the de-facto streaming format and is provider-agnostic in practice
58
+ * (many vendors expose OpenAI-compatible endpoints). Backends that wrap other
59
+ * providers (e.g. Anthropic) should translate their native SSE into this
60
+ * shape so the hook stays unchanged.
49
61
  *
50
62
  * @returns `stream`: a function that takes a prompt and streaming callback.
51
63
  */
52
- var useOpenAIStream = function (url, authToken) {
64
+ var useTextAIStream = function (url, authToken) {
53
65
  var abortRef = useRef(null);
54
66
  /**
55
67
  * Streams a prompt to a backend AI endpoint and handles streamed chunks.
@@ -92,7 +104,7 @@ var useOpenAIStream = function (url, authToken) {
92
104
  case 1:
93
105
  response = _f.sent();
94
106
  if (!response.ok) {
95
- console.error("OpenAI stream request failed: ".concat(response.statusText));
107
+ console.error("Text AI stream request failed: ".concat(response.statusText));
96
108
  return [2 /*return*/];
97
109
  }
98
110
  if (!response.body) {
@@ -159,4 +171,4 @@ var useOpenAIStream = function (url, authToken) {
159
171
  }, []);
160
172
  return { stream: stream, cancel: cancel };
161
173
  };
162
- export { useOpenAIStream };
174
+ export { useTextAIStream };
@@ -23,18 +23,25 @@ var Selector = function (props) {
23
23
  setYear(props.value ? props.value.getFullYear().toString() : "");
24
24
  }
25
25
  }, [props.value]);
26
- // Callback when day, month or year changes.
27
- React.useEffect(function () {
28
- var numYear = parseInt(year);
29
- var numMonth = parseInt(month);
30
- var numDay = parseInt(day);
31
- if (isNaN(numYear) || isNaN(numMonth) || isNaN(numYear))
26
+ // Emit synchronously from the onChange handler rather than via a useEffect
27
+ // on [day, month, year]. Passive effects don't run until after paint, so
28
+ // an effect-based emit can be skipped if the next browser event (e.g. a
29
+ // click on Save) arrives before React's effect phase — losing the
30
+ // most-recent keystroke.
31
+ var emit = function (d, m, y) {
32
+ var numDay = parseInt(d);
33
+ var numMonth = parseInt(m);
34
+ var numYear = parseInt(y);
35
+ if (isNaN(numDay) || isNaN(numMonth) || isNaN(numYear))
32
36
  return;
33
37
  var date = new Date(numYear, numMonth - 1, numDay);
34
38
  // Overwrite year, because Date.new converts 0-99 to 1900-1999.
35
39
  date.setFullYear(numYear);
36
40
  props.onChange(date);
37
- }, [day, month, year]);
41
+ };
42
+ var handleDayChange = function (v) { setDay(v); emit(v, month, year); };
43
+ var handleMonthChange = function (v) { setMonth(v); emit(day, v, year); };
44
+ var handleYearChange = function (v) { setYear(v); emit(day, month, v); };
38
45
  // Move focus to month input.
39
46
  var focusMonth = function () {
40
47
  monthRef.current.focus();
@@ -46,10 +53,10 @@ var Selector = function (props) {
46
53
  yearRef.current.select();
47
54
  };
48
55
  return (React.createElement(InputWrapper, { fluid: true, error: props.error, disabled: props.disabled, ghost: props.ghost, transparent: props.transparent, onClear: (props.clearable && props.value) ? props.onClear : null, icon: { url: SVG.Icons.Calendar, color: theme.colors.primary[3], onClick: props.onCalendar }, iconPosition: 'right' },
49
- React.createElement(NumericInput, { disabled: props.disabled, width: 38, maxLength: 2, placeholder: "dd", value: day, onChange: setDay, onBlur: focusMonth }),
56
+ React.createElement(NumericInput, { disabled: props.disabled, width: 38, maxLength: 2, placeholder: "dd", value: day, onChange: handleDayChange, onBlur: focusMonth }),
50
57
  "/",
51
- React.createElement(NumericInput, { disabled: props.disabled, width: 38, maxLength: 2, placeholder: "mm", value: month, onChange: setMonth, ref: monthRef, onBlur: focusYear }),
58
+ React.createElement(NumericInput, { disabled: props.disabled, width: 38, maxLength: 2, placeholder: "mm", value: month, onChange: handleMonthChange, ref: monthRef, onBlur: focusYear }),
52
59
  "/",
53
- React.createElement(NumericInput, { disabled: props.disabled, width: 44, maxLength: 4, placeholder: "yyyy", value: year, onChange: setYear, ref: yearRef })));
60
+ React.createElement(NumericInput, { disabled: props.disabled, width: 44, maxLength: 4, placeholder: "yyyy", value: year, onChange: handleYearChange, ref: yearRef })));
54
61
  };
55
62
  export { Selector };
@@ -50,15 +50,16 @@ interface IEditorProps extends ITestable {
50
50
  */
51
51
  codeButtons?: boolean;
52
52
  /**
53
- * URL for OpenAI requests.
54
- * If not present, no OpenAI controls will be available.
53
+ * URL for the back-end Text AI endpoint (streamed text generation).
54
+ * If not present, no Text AI controls will be available.
55
55
  */
56
- openAIurl?: string;
56
+ textAIurl?: string;
57
57
  /**
58
- * URL for AssemblyAI temporary token requests.
59
- * If not present, no AssemblyAI controls will be available.
58
+ * URL for the back-end Speech AI endpoint (issues a temporary streaming
59
+ * token for the speech-to-text provider).
60
+ * If not present, no Speech AI controls will be available.
60
61
  */
61
- assemblyAIurl?: string;
62
+ speechAIurl?: string;
62
63
  /**
63
64
  * Optional Bearer token for authenticated AI requests.
64
65
  */
@@ -91,7 +91,7 @@ var EditorBase = function (props) {
91
91
  React.createElement("div", { "data-testid": props['data-testid'] || "Editor", className: props.className, ref: wrapperRef },
92
92
  React.createElement(OverlayScrollbarsComponent, { className: "scroller ".concat(fullscreen ? 'fullscreen' : ''), defer: true, options: { scrollbars: { theme: 'os-theme-dark', autoHide: 'leave' } } },
93
93
  !props.disabled && !props.ghost &&
94
- React.createElement(MenuBar, { allowFullscreen: props.allowFullscreen, codeButtons: props.codeButtons, fullscreen: fullscreen, editor: editor, onToggleFullscreen: handleToggleFullscreen, openAIurl: props.openAIurl, assemblyAIurl: props.assemblyAIurl, authToken: props.authToken }),
94
+ React.createElement(MenuBar, { allowFullscreen: props.allowFullscreen, codeButtons: props.codeButtons, fullscreen: fullscreen, editor: editor, onToggleFullscreen: handleToggleFullscreen, textAIurl: props.textAIurl, speechAIurl: props.speechAIurl, authToken: props.authToken }),
95
95
  React.createElement(EditorContent, { editor: editor, ref: setRef })))));
96
96
  };
97
97
  var pulse = keyframes(templateObject_1 || (templateObject_1 = __makeTemplateObject(["\n from {\n opacity: .15;\n }\n to {\n opacity: 1;\n }\n"], ["\n from {\n opacity: .15;\n }\n to {\n opacity: 1;\n }\n"])));
@@ -8,5 +8,5 @@ interface IProps {
8
8
  */
9
9
  authToken?: string;
10
10
  }
11
- declare const SpeechButton: (props: IProps) => React.JSX.Element;
12
- export { SpeechButton };
11
+ declare const SpeechAIButton: (props: IProps) => React.JSX.Element;
12
+ export { SpeechAIButton };
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import { MenuButton } from '../menu/MenuButton';
3
3
  import { SVG } from '../../../svg';
4
- import { useAssemblyAIRecorder } from '../../../hooks/useAssemblyAIRecorder';
5
- var SpeechButton = function (props) {
4
+ import { useSpeechAIRecorder } from '../../../hooks/useSpeechAIRecorder';
5
+ var SpeechAIButton = function (props) {
6
6
  var fromRef = useRef(null);
7
7
  var transcriptRef = useRef("");
8
8
  var onTranscript = function (text) {
@@ -12,7 +12,7 @@ var SpeechButton = function (props) {
12
12
  view.dispatch(transaction);
13
13
  transcriptRef.current = text;
14
14
  };
15
- var _a = useAssemblyAIRecorder(props.url, onTranscript, props.authToken), recordingStatus = _a.recordingStatus, toggleRecording = _a.toggleRecording;
15
+ var _a = useSpeechAIRecorder(props.url, onTranscript, props.authToken), recordingStatus = _a.recordingStatus, toggleRecording = _a.toggleRecording;
16
16
  useEffect(function () {
17
17
  if (recordingStatus == 'recording') {
18
18
  props.editor.setOptions({ editable: false });
@@ -26,6 +26,6 @@ var SpeechButton = function (props) {
26
26
  var handleClick = function () {
27
27
  toggleRecording();
28
28
  };
29
- return (React.createElement(MenuButton, { editor: props.editor, hint: React.createElement("span", { style: { whiteSpace: "nowrap" } }, "AssemblyAI speech-to-text"), onClick: handleClick, disabled: false, active: recordingStatus == 'connecting', highlighted: recordingStatus == 'recording', icon: { url: SVG.Icons.Microphone } }));
29
+ return (React.createElement(MenuButton, { editor: props.editor, hint: React.createElement("span", { style: { whiteSpace: "nowrap" } }, "Speech-to-text"), onClick: handleClick, disabled: false, active: recordingStatus == 'connecting', highlighted: recordingStatus == 'recording', icon: { url: SVG.Icons.Microphone } }));
30
30
  };
31
- export { SpeechButton };
31
+ export { SpeechAIButton };
@@ -0,0 +1,28 @@
1
+ import React from 'react';
2
+ import { Editor } from '@tiptap/react';
3
+ interface IProps {
4
+ editor: Editor;
5
+ /**
6
+ * URL for AI requests.
7
+ */
8
+ url: string;
9
+ /**
10
+ * Optional Bearer token for authenticated requests.
11
+ */
12
+ authToken?: string;
13
+ }
14
+ /**
15
+ * `TextAIButton` provides a menu button that allows the user to run various
16
+ * AI-powered text transformations on selected editor content.
17
+ *
18
+ * Available transformations include:
19
+ * - Summarizing selected content
20
+ * - Extracting key points
21
+ * - Translating
22
+ *
23
+ * When triggered, the selected content is cut, sent to the configured Text AI
24
+ * back-end, and replaced with the transformed version. Streaming support
25
+ * allows real-time insertion with animated progress.
26
+ */
27
+ declare const TextAIButton: (props: IProps) => React.JSX.Element;
28
+ export { TextAIButton };
@@ -37,24 +37,23 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
37
37
  import React, { useState } from 'react';
38
38
  import { marked } from 'marked';
39
39
  import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model';
40
- import { useOpenAIStream } from '../../../hooks/useOpenAIStream';
41
- import { OpenAIMenu } from './OpenAIMenu';
40
+ import { useTextAIStream } from '../../../hooks/useTextAIStream';
41
+ import { TextAIMenu } from './TextAIMenu';
42
42
  /**
43
- * `AIButton` provides a menu button that allows the user to run various
44
- * AI-powered transformations on selected editor content.
43
+ * `TextAIButton` provides a menu button that allows the user to run various
44
+ * AI-powered text transformations on selected editor content.
45
45
  *
46
46
  * Available transformations include:
47
- * - Telling a joke
48
47
  * - Summarizing selected content
49
48
  * - Extracting key points
50
- * - Translating to Portuguese
49
+ * - Translating
51
50
  *
52
- * When triggered, the selected content is cut, sent to OpenAI, and replaced
53
- * with the transformed version. Streaming support allows real-time insertion
54
- * with animated progress.
51
+ * When triggered, the selected content is cut, sent to the configured Text AI
52
+ * back-end, and replaced with the transformed version. Streaming support
53
+ * allows real-time insertion with animated progress.
55
54
  */
56
- var OpenAIButton = function (props) {
57
- var streamOpenAI = useOpenAIStream(props.url, props.authToken).stream;
55
+ var TextAIButton = function (props) {
56
+ var streamTextAI = useTextAIStream(props.url, props.authToken).stream;
58
57
  var _a = useState(false), streaming = _a[0], setStreaming = _a[1];
59
58
  // The DOM parser is only created once, then used many times as content
60
59
  // is streamed in.
@@ -154,7 +153,7 @@ var OpenAIButton = function (props) {
154
153
  // Create initial node.
155
154
  _a.sent();
156
155
  text = "";
157
- return [4 /*yield*/, streamOpenAI(prompt, function (chunk) { return __awaiter(void 0, void 0, void 0, function () {
156
+ return [4 /*yield*/, streamTextAI(prompt, function (chunk) { return __awaiter(void 0, void 0, void 0, function () {
158
157
  return __generator(this, function (_a) {
159
158
  switch (_a.label) {
160
159
  case 0:
@@ -203,6 +202,6 @@ var OpenAIButton = function (props) {
203
202
  props.editor.setOptions({ editable: true });
204
203
  });
205
204
  };
206
- return (React.createElement(OpenAIMenu, { editor: props.editor, onClick: function (p) { return transformStream(p); }, animated: streaming }));
205
+ return (React.createElement(TextAIMenu, { editor: props.editor, onClick: function (p) { return transformStream(p); }, animated: streaming }));
207
206
  };
208
- export { OpenAIButton };
207
+ export { TextAIButton };
@@ -5,5 +5,5 @@ interface IProps {
5
5
  onClick: (prompt: (text: string) => string) => void;
6
6
  animated: boolean;
7
7
  }
8
- declare const OpenAIMenu: (props: IProps) => React.JSX.Element;
9
- export { OpenAIMenu };
8
+ declare const TextAIMenu: (props: IProps) => React.JSX.Element;
9
+ export { TextAIMenu };
@@ -4,7 +4,7 @@ import { MenuButton } from '../menu/MenuButton';
4
4
  import { List, ListRow } from '../../../containers/List';
5
5
  import { SVG } from '../../../svg';
6
6
  import { Icon } from '../../../controls/Icon';
7
- var OpenAIMenu = function (props) {
7
+ var TextAIMenu = function (props) {
8
8
  var _a = React.useState(false), open = _a[0], setOpen = _a[1];
9
9
  var _b = React.useState(false), langOpen = _b[0], setLangOpen = _b[1];
10
10
  // With an empty selection, some AI commands cannot work, because they
@@ -19,7 +19,7 @@ var OpenAIMenu = function (props) {
19
19
  setLangOpen(false);
20
20
  }, [open]);
21
21
  var languages = ["US English", "UK English", "Portuguese", "Spanish", "French", "German", "Dutch", "Simplified Chinese"];
22
- return (React.createElement(Openable, { width: 300, toggle: React.createElement(MenuButton, { editor: props.editor, onClick: function () { return setOpen(!open); }, hint: React.createElement("span", { style: { whiteSpace: "nowrap" } }, "OpenAI functions"), icon: { url: SVG.Editor.AI, animated: props.animated } }), content: React.createElement("div", { style: { display: 'flex', gap: '8px' } },
22
+ return (React.createElement(Openable, { width: 300, toggle: React.createElement(MenuButton, { editor: props.editor, onClick: function () { return setOpen(!open); }, hint: React.createElement("span", { style: { whiteSpace: "nowrap" } }, "Text AI"), icon: { url: SVG.Editor.AI, animated: props.animated } }), content: React.createElement("div", { style: { display: 'flex', gap: '8px' } },
23
23
  React.createElement("div", { style: { minWidth: '150px' } },
24
24
  React.createElement(List, { maxItems: 4, contract: true },
25
25
  React.createElement(ListRow, { disabled: selectionIsEmpty, onClick: function () { return handleClick(function (text) {
@@ -39,4 +39,4 @@ var OpenAIMenu = function (props) {
39
39
  }); } }, lang);
40
40
  })))), open: open, onClose: function () { return setOpen(false); } }));
41
41
  };
42
- export { OpenAIMenu };
42
+ export { TextAIMenu };
@@ -16,15 +16,15 @@ interface IProps {
16
16
  */
17
17
  codeButtons?: boolean;
18
18
  /**
19
- * URL for OpenAI requests.
20
- * If not present, no AI controls will be available.
19
+ * URL for the back-end Text AI endpoint.
20
+ * If not present, no Text AI controls will be available.
21
21
  */
22
- openAIurl?: string;
22
+ textAIurl?: string;
23
23
  /**
24
- * URL for AssemblyAI requests.
25
- * If not present, no AI controls will be available.
24
+ * URL for the back-end Speech AI endpoint.
25
+ * If not present, no Speech AI controls will be available.
26
26
  */
27
- assemblyAIurl?: string;
27
+ speechAIurl?: string;
28
28
  /**
29
29
  * Optional Bearer token for authenticated AI requests.
30
30
  */
@@ -13,8 +13,8 @@ import { OrderedListButton } from '../buttons/OrderedListButton';
13
13
  import { BulletListButton } from '../buttons/BulletListButton';
14
14
  import { CodeBlockButton } from '../buttons/CodeBlockButton';
15
15
  import { FullscreenButton } from '../buttons/FullscreenButton';
16
- import { OpenAIButton } from '../buttons/OpenAIButton';
17
- import { SpeechButton } from '../buttons/SpeechButton';
16
+ import { TextAIButton } from '../buttons/TextAIButton';
17
+ import { SpeechAIButton } from '../buttons/SpeechAIButton';
18
18
  var MenuBarBase = function (props) {
19
19
  if (!props.editor) {
20
20
  return null;
@@ -31,11 +31,11 @@ var MenuBarBase = function (props) {
31
31
  React.createElement(MenuSeparator, null),
32
32
  React.createElement(CodeButton, { editor: props.editor }),
33
33
  React.createElement(CodeBlockButton, { editor: props.editor })),
34
- (props.openAIurl || props.allowFullscreen) && React.createElement(MenuSeparator, null),
35
- props.openAIurl &&
36
- React.createElement(OpenAIButton, { url: props.openAIurl, editor: props.editor, authToken: props.authToken }),
37
- props.assemblyAIurl &&
38
- React.createElement(SpeechButton, { url: props.assemblyAIurl, editor: props.editor, authToken: props.authToken }),
34
+ (props.textAIurl || props.allowFullscreen) && React.createElement(MenuSeparator, null),
35
+ props.textAIurl &&
36
+ React.createElement(TextAIButton, { url: props.textAIurl, editor: props.editor, authToken: props.authToken }),
37
+ props.speechAIurl &&
38
+ React.createElement(SpeechAIButton, { url: props.speechAIurl, editor: props.editor, authToken: props.authToken }),
39
39
  props.allowFullscreen &&
40
40
  React.createElement(FullscreenButton, { fullscreen: props.fullscreen, editor: props.editor, onClick: props.onToggleFullscreen }))));
41
41
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@longline/aqua-ui",
3
- "version": "1.0.331",
3
+ "version": "1.0.335",
4
4
  "description": "AquaUI",
5
5
  "author": "Alexander van Oostenrijk / Longline Environment",
6
6
  "license": "Commercial",
@@ -17,9 +17,9 @@
17
17
  },
18
18
  "scripts": {
19
19
  "build": "webpack --config webpack.config.js && tsc --version && tsc",
20
- "storybook": "cross-env OPENAI_RESPONSE_URL=http://api.flow/api/ai/openai/response-stream OPENAI_AUDIO_URL=http://api.flow/api/ai/openai/audio OPENAI_TRANSCRIBE_URL=http://api.flow/api/ai/openai/transcribe ASSEMBLYAI_TOKEN_URL=http://api.flow/api/ai/assemblyai/get-token storybook dev -p 6006",
21
- "build-storybook": "cross-env OPENAI_RESPONSE_URL=http://api.flow/api/ai/openai/response-stream OPENAI_AUDIO_URL=http://api.flow/api/ai/openai/audio OPENAI_TRANSCRIBE_URL=http://api.flow/api/ai/openai/transcribe ASSEMBLYAI_TOKEN_URL=http://api.flow/api/ai/assemblyai/get-token storybook build",
22
- "build-storybook-gitlab": "cross-env OPENAI_RESPONSE_URL=https://aquarisk.dev.longline.uk/api/ai/openai/response-stream OPENAI_AUDIO_URL=https://aquarisk.dev.longline.uk/api/ai/openai/audio OPENAI_TRANSCRIBE_URL=https://aquarisk.dev.longline.uk/api/ai/openai/transcribe ASSEMBLYAI_TOKEN_URL=https://aquarisk.dev.longline.uk/api/ai/assemblyai/get-token storybook build",
20
+ "storybook": "cross-env TEXTAI_RESPONSE_URL=http://api.flow/api/ai/anthropic/stream SPEECHAI_TOKEN_URL=http://api.flow/api/ai/assemblyai/get-token storybook dev -p 6006",
21
+ "build-storybook": "cross-env TEXTAI_RESPONSE_URL=http://api.flow/api/ai/anthropic/stream SPEECHAI_TOKEN_URL=http://api.flow/api/ai/assemblyai/get-token storybook build",
22
+ "build-storybook-gitlab": "cross-env TEXTAI_RESPONSE_URL=https://aquarisk.dev.longline.uk/api/ai/anthropic/stream SPEECHAI_TOKEN_URL=https://aquarisk.dev.longline.uk/api/ai/assemblyai/get-token storybook build",
23
23
  "tsc": "tsc",
24
24
  "pub": "npm version patch && webpack --config webpack.config.js && tsc && cd dist && npm publish"
25
25
  },
@@ -58,7 +58,6 @@
58
58
  "gl-matrix": "^3.4.3",
59
59
  "mapbox-gl": "^3.9.4",
60
60
  "marked": "^15.0.12",
61
- "openai": "^5.0.1",
62
61
  "overlayscrollbars-react": "^0.5.6",
63
62
  "react": "^18.3.1",
64
63
  "react-dom": "^18.3.1",
@@ -1 +0,0 @@
1
- export { useAssemblyAIRecorder } from './useAssemblyAIRecorder';
@@ -1 +0,0 @@
1
- export { useAssemblyAIRecorder } from './useAssemblyAIRecorder';
@@ -1,6 +0,0 @@
1
- import React from 'react';
2
- interface IProps {
3
- url: string;
4
- }
5
- declare const AssemblyAudioInput: (props: IProps) => React.JSX.Element;
6
- export { AssemblyAudioInput };
@@ -1,27 +0,0 @@
1
- type TranscriptCallback = (text: string) => void;
2
- type TRecordingStatus = 'idle' | 'connecting' | 'recording';
3
- /**
4
- * useAssemblyAIRecorder
5
- *
6
- * A custom React hook for real-time audio transcription using AssemblyAI's
7
- * streaming API.
8
- *
9
- * When recording is toggled on, it takes a few moments to establish a web
10
- * socket connection. During this time, the recording status will be
11
- * `'connecting'`.
12
- *
13
- * @param url - Back-end URL for obtaining temporary token
14
- * @param onTranscript - Callback that receives updated transcript text.
15
- * @returns An object containing:
16
- * - recordingStatus: 'idle' | 'connecting' | 'recording'
17
- * - toggleRecording: function to start or stop recording
18
- *
19
- * The `SpeechManager` singleton is use to keep track of which
20
- * AssemblyAIRecorder is currently recording; when a new recording is started,
21
- * the current one is stopped.
22
- */
23
- declare const useAssemblyAIRecorder: (url: string, onTranscript: TranscriptCallback, authToken?: string) => {
24
- recordingStatus: TRecordingStatus;
25
- toggleRecording: () => Promise<void>;
26
- };
27
- export { useAssemblyAIRecorder, TRecordingStatus };
@@ -1 +0,0 @@
1
- export { useOpenAIRecorder } from './useOpenAIRecorder';
@@ -1 +0,0 @@
1
- export { useOpenAIRecorder } from './useOpenAIRecorder';
@@ -1,8 +0,0 @@
1
- import * as React from 'react';
2
- interface IProps {
3
- }
4
- /**
5
- * The extended media recorder allows converting to .wav format.
6
- */
7
- declare const AudioInput: (props: IProps) => React.JSX.Element;
8
- export { AudioInput };