polyfire-js
Advanced tools
Comparing version 0.2.34 to 0.2.35
@@ -1,5 +0,5 @@ | ||
import React from "react"; | ||
import React, { CSSProperties } from "react"; | ||
export interface AutoCompleteInputProps extends React.InputHTMLAttributes<HTMLInputElement> { | ||
onChange?: React.InputHTMLAttributes<HTMLInputElement>["onChange"]; | ||
completionColor?: CSSProperties["color"]; | ||
} | ||
export declare function AutoCompleteInput({ onChange: onChangeProps, ...props }: AutoCompleteInputProps): React.ReactElement; | ||
export declare function AutoCompleteInput(props: AutoCompleteInputProps): React.ReactElement; |
@@ -36,2 +36,38 @@ "use strict"; | ||
}; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (g && (g = 0, op[0] && (_ = 0)), _) try { | ||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var __rest = (this && this.__rest) || function (s, e) { | ||
@@ -52,128 +88,130 @@ var t = {}; | ||
var hooks_1 = require("../hooks"); | ||
function AutoCompleteInput(_a) { | ||
var _b, _c, _d; | ||
var onChangeProps = _a.onChange, props = __rest(_a, ["onChange"]); | ||
var _e = (0, hooks_1.usePolyfire)(), status = _e.auth.status, generate = _e.models.generate; | ||
var _f = (0, react_1.useState)(), prompt = _f[0], setPrompt = _f[1]; | ||
var _g = (0, react_1.useState)(), scroll = _g[0], setScroll = _g[1]; | ||
var _h = (0, react_1.useState)(), completion = _h[0], setCompletion = _h[1]; | ||
var _j = (0, react_1.useState)(null), previousTimeout = _j[0], setPreviousTimeout = _j[1]; | ||
var _k = (0, react_1.useState)(null), previousGeneration = _k[0], setPreviousGeneration = _k[1]; | ||
(0, react_1.useEffect)(function () { | ||
// We need to stop the previous generations and timers to prevent the previous | ||
// to appear after a change. | ||
if (previousTimeout !== null) { | ||
clearTimeout(previousTimeout); | ||
var containerStyle = { | ||
maxWidth: "100%", | ||
border: "1px solid #ccc", | ||
minHeight: "2rem", | ||
position: "relative", | ||
alignItems: "center", | ||
display: "flex", | ||
overflow: "hidden", | ||
}; | ||
var inputStyle = { | ||
minHeight: "2rem", | ||
width: "100%", | ||
lineHeight: "2rem", | ||
paddingLeft: "0.25rem", | ||
paddingTop: "0", | ||
paddingBottom: "0", | ||
border: "none", | ||
overflow: "hidden", | ||
}; | ||
var completionStyle = { | ||
position: "absolute", | ||
display: "flex", | ||
pointerEvents: "none", | ||
width: "auto", | ||
lineHeight: "2rem", | ||
paddingLeft: "0.5rem", | ||
}; | ||
var hiddenSizerStyle = { | ||
visibility: "hidden", | ||
position: "absolute", | ||
whiteSpace: "pre", | ||
maxWidth: "100%", | ||
overflow: "hidden", | ||
}; | ||
function AutoCompleteInput(props) { | ||
var _this = this; | ||
var _a = props.completionColor, completionColor = _a === void 0 ? "grey" : _a, _b = props.className, className = _b === void 0 ? "" : _b, inputProps = __rest(props, ["completionColor", "className"]); | ||
var _c = (0, hooks_1.usePolyfire)(), status = _c.auth.status, generate = _c.models.generate; | ||
var _d = (0, react_1.useState)(""), prompt = _d[0], setPrompt = _d[1]; | ||
var _e = (0, react_1.useState)(""), completion = _e[0], setCompletion = _e[1]; | ||
// This state is used to wait for the user to start typing again before initiating a new completion | ||
var _f = (0, react_1.useState)(false), completionUsed = _f[0], setCompletionUsed = _f[1]; | ||
var inputRef = (0, react_1.useRef)(null); | ||
var hiddenSizerRef = (0, react_1.useRef)(null); | ||
var completionRef = (0, react_1.useRef)(null); | ||
var generationRef = (0, react_1.useRef)(null); | ||
var updateCompletionPosition = function () { | ||
if (inputRef.current && hiddenSizerRef.current && completionRef.current) { | ||
hiddenSizerRef.current.innerText = prompt; | ||
var textWidth = hiddenSizerRef.current.offsetWidth; | ||
completionRef.current.style.left = "".concat(textWidth, "px"); | ||
} | ||
if (previousGeneration) { | ||
try { | ||
previousGeneration.stop(); | ||
previousGeneration.destroy(); | ||
setPreviousGeneration(null); | ||
}; | ||
var generateCompletion = (0, react_1.useCallback)(function () { return __awaiter(_this, void 0, void 0, function () { | ||
var systemPrompt, generation, _a, error_1; | ||
return __generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
if (status !== "authenticated" || !prompt || completionUsed) | ||
return [2 /*return*/]; | ||
_b.label = 1; | ||
case 1: | ||
_b.trys.push([1, 3, , 4]); | ||
systemPrompt = "Your role is to predict what the user will type next. \n Don't answer the user because there is only one user who speaks. \n Only answer with the prediction until the end of the sentence. \n Don't explain anything about the prediction. \n Don't repeat what the user said. \n Complete from where the user stopped typing. \n Just answer only one or two words (never more words)."; | ||
generation = generate(prompt, { systemPrompt: systemPrompt, autoComplete: true }); | ||
generationRef.current = generation; | ||
_a = setCompletion; | ||
return [4 /*yield*/, generation]; | ||
case 2: | ||
_a.apply(void 0, [_b.sent()]); | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
error_1 = _b.sent(); | ||
console.error("Error generating completion:", error_1); | ||
return [3 /*break*/, 4]; | ||
case 4: return [2 /*return*/]; | ||
} | ||
catch (_) { | ||
// For some reason, previousGeneration.stop seems to throw an error | ||
// when pasting some text. It might be due to a race condition if | ||
// multiple letters are typed too fast causing the previousGeneration.stop() | ||
// to be called multiple time on the same one ? | ||
// In any case, this error is unimportant and can be dismissed. | ||
}); | ||
}); }, [status, generate, prompt, completionUsed]); | ||
(0, react_1.useEffect)(function () { | ||
var timer = setTimeout(generateCompletion, 500); | ||
updateCompletionPosition(); | ||
return function () { | ||
clearTimeout(timer); | ||
// We need to stop the previous generations and timers to prevent the previous | ||
// to appear after a change. | ||
if (generationRef.current) { | ||
try { | ||
generationRef.current.stop(); | ||
} | ||
catch (error) { | ||
console.error("Error stopping generation:", error); | ||
} | ||
generationRef.current = null; | ||
} | ||
} | ||
if (status === "authenticated" && prompt) { | ||
setPreviousTimeout( | ||
// We wait 1 second wihout anything typed before completing anything | ||
// to avoid sending too many requests for nothing. | ||
setTimeout(function () { | ||
var generation = generate(prompt, { | ||
systemPrompt: "Your role is to predict what the user will type next.\n Don't answer the user.\n Only answer with the prediction until the end of the sentence.\n Don't explain anything about the prediction. Don't repeat what the user said.\n Complete from the where the user stopped typing.", | ||
autoComplete: true, | ||
}); | ||
setPreviousGeneration(generation); | ||
generation.then(setCompletion); | ||
}, 1000)); | ||
} | ||
}, [status, generate, prompt]); | ||
var inputRef = react_1.default.useRef(null); | ||
var outputRef = react_1.default.useRef(null); | ||
var onChange = (0, react_1.useCallback)(function (e) { | ||
setPrompt(e.target.value); | ||
var _a = e.target, scrollTop = _a.scrollTop, scrollLeft = _a.scrollLeft; | ||
setScroll({ scrollTop: scrollTop, scrollLeft: scrollLeft }); | ||
}; | ||
}, [generateCompletion, prompt]); | ||
var onChange = function (e) { | ||
var _a; | ||
setPrompt(e.target.value || ""); | ||
setCompletion(""); | ||
if (onChangeProps) { | ||
onChangeProps(e); | ||
} | ||
}, [onChangeProps]); | ||
setCompletionUsed(false); | ||
updateCompletionPosition(); | ||
(_a = props.onChange) === null || _a === void 0 ? void 0 : _a.call(props, e); | ||
}; | ||
var onKeyDown = (0, react_1.useCallback)(function (e) { | ||
if (e.key === "Tab" && completion) { | ||
e.preventDefault(); | ||
setPrompt(function (p) { | ||
if (inputRef.current && outputRef.current) { | ||
inputRef.current.value = p + completion || ""; | ||
// We fully scroll left after a completion | ||
inputRef.current.scrollLeft = 99999999; | ||
outputRef.current.scrollLeft = 99999999; | ||
} | ||
return p + completion; | ||
setPrompt(function (prev) { | ||
var updatedPrompt = prev + completion; | ||
setTimeout(function () { | ||
if (inputRef.current) { | ||
// We fully scroll left after a completion | ||
inputRef.current.scrollLeft = 99999999; | ||
inputRef.current.setSelectionRange(updatedPrompt.length, updatedPrompt.length); | ||
} | ||
}, 0); | ||
return updatedPrompt; | ||
}); | ||
setCompletion(""); | ||
setCompletionUsed(true); | ||
} | ||
}, [completion]); | ||
// We have to manually check that the scroll stays synchronized between the | ||
// div and the input | ||
var onScroll = (0, react_1.useCallback)(function (e) { | ||
var _a = e.target, scrollTop = _a.scrollTop, scrollLeft = _a.scrollLeft; | ||
setScroll({ scrollTop: scrollTop, scrollLeft: scrollLeft }); | ||
}, []); | ||
(0, react_1.useEffect)(function () { | ||
if (outputRef.current && scroll) { | ||
outputRef.current.scrollLeft = scroll.scrollLeft; | ||
outputRef.current.scrollTop = scroll.scrollTop; | ||
} | ||
}, [scroll]); | ||
// And we need to also manually synchronize the output size and position to | ||
// the input, to be sure it stays in the same place even if the style set in | ||
// the props gets too weird | ||
(0, react_1.useEffect)(function () { | ||
if (outputRef.current && inputRef.current) { | ||
outputRef.current.style.paddingLeft = "".concat(inputRef.current.getBoundingClientRect().left - | ||
outputRef.current.getBoundingClientRect().left, "px"); | ||
outputRef.current.style.paddingTop = "".concat(inputRef.current.getBoundingClientRect().top - | ||
outputRef.current.getBoundingClientRect().top, "px"); | ||
outputRef.current.style.width = "".concat(inputRef.current.getBoundingClientRect().width, "px"); | ||
} | ||
}, [outputRef, inputRef]); | ||
return (react_1.default.createElement("label", { style: __assign(__assign({ display: "inline-block", minHeight: 16, minWidth: 176, fontSize: 13.33333, padding: "1px 2px", border: "2px solid #ccc", borderRadius: "3px", backgroundColor: "white" }, (props.style || {})), { position: "relative" }), className: props.className }, | ||
react_1.default.createElement("div", { style: { | ||
fontFamily: ((_b = props === null || props === void 0 ? void 0 : props.style) === null || _b === void 0 ? void 0 : _b.fontFamily) || "Cantarell", | ||
backgroundColor: "transparent", | ||
fontSize: "inherit", | ||
position: "absolute", | ||
color: "inherit", | ||
maxWidth: "inherit", | ||
width: "100%", | ||
height: "inherit", | ||
overflow: "hidden", | ||
padding: "0", | ||
display: "inline", | ||
whiteSpace: "pre", | ||
}, ref: outputRef }, | ||
prompt, | ||
react_1.default.createElement("span", { style: { color: "grey" } }, completion)), | ||
react_1.default.createElement("input", __assign({}, props, { style: { | ||
fontFamily: ((_c = props === null || props === void 0 ? void 0 : props.style) === null || _c === void 0 ? void 0 : _c.fontFamily) || "Cantarell", | ||
caretColor: ((_d = props === null || props === void 0 ? void 0 : props.style) === null || _d === void 0 ? void 0 : _d.caretColor) || "black", | ||
fontSize: "inherit", | ||
position: "relative", | ||
width: "inherit", | ||
minWidth: "100%", | ||
maxWidth: "100%", | ||
height: "inherit", | ||
padding: 0, | ||
border: 0, | ||
display: "inline-block", | ||
color: "transparent", | ||
backgroundColor: "transparent", | ||
}, onChange: onChange, onScrollCapture: onScroll, onSelectCapture: onScroll, onKeyDown: onKeyDown, ref: inputRef })))); | ||
return (react_1.default.createElement("div", { style: containerStyle, className: className }, | ||
react_1.default.createElement("input", __assign({}, inputProps, { ref: inputRef, value: prompt, onChange: onChange, onKeyDown: onKeyDown, style: inputStyle, className: className })), | ||
react_1.default.createElement("span", { ref: hiddenSizerRef, style: hiddenSizerStyle }), | ||
react_1.default.createElement("span", { ref: completionRef, style: __assign(__assign({}, completionStyle), { color: completionColor }) }, completion))); | ||
} | ||
exports.AutoCompleteInput = AutoCompleteInput; |
{ | ||
"name": "polyfire-js", | ||
"version": "0.2.34", | ||
"version": "0.2.35", | ||
"main": "index.js", | ||
@@ -5,0 +5,0 @@ "types": "index.d.ts", |
270169
5501