@pupt/react 0.0.1 → 1.2.0

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/dist/index.js ADDED
@@ -0,0 +1,1060 @@
1
+ import { jsx, Fragment } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback, createContext, useMemo, useContext } from "react";
3
+ import { Pupt, createInputIterator, isPuptElement, createPromptFromSource, CHILDREN, createEnvironment, render, createSearchEngine, evaluateFormula } from "@pupt/lib";
4
+ import { CHILDREN as CHILDREN2, COMPONENT_MARKER, Component, DEFERRED_REF, PROPS, TYPE, createRuntimeConfig, evaluateFormula as evaluateFormula2, isComponentClass, isDeferredRef, isPuptElement as isPuptElement2 } from "@pupt/lib";
5
+ function usePuptLibrary(options = {}) {
6
+ const { modules: initialModules = [], searchConfig } = options;
7
+ const [modules, setModules] = useState(initialModules);
8
+ const [isLoading, setIsLoading] = useState(false);
9
+ const [error, setError] = useState(null);
10
+ const [prompts, setPrompts] = useState([]);
11
+ const [tags, setTags] = useState([]);
12
+ const puptRef = useRef(null);
13
+ const searchConfigRef = useRef(searchConfig);
14
+ searchConfigRef.current = searchConfig;
15
+ useEffect(() => {
16
+ let cancelled = false;
17
+ async function init() {
18
+ setIsLoading(true);
19
+ setError(null);
20
+ try {
21
+ const config = { modules };
22
+ if (searchConfigRef.current) {
23
+ config.searchConfig = searchConfigRef.current;
24
+ }
25
+ const pupt = new Pupt(config);
26
+ await pupt.init();
27
+ if (cancelled) {
28
+ return;
29
+ }
30
+ puptRef.current = pupt;
31
+ setPrompts(pupt.getPrompts());
32
+ setTags(pupt.getTags());
33
+ } catch (err) {
34
+ if (cancelled) {
35
+ return;
36
+ }
37
+ setError(err instanceof Error ? err : new Error(String(err)));
38
+ setPrompts([]);
39
+ setTags([]);
40
+ } finally {
41
+ if (!cancelled) {
42
+ setIsLoading(false);
43
+ }
44
+ }
45
+ }
46
+ void init();
47
+ return () => {
48
+ cancelled = true;
49
+ };
50
+ }, [modules]);
51
+ const getPrompt = useCallback(
52
+ (name) => {
53
+ var _a;
54
+ return (_a = puptRef.current) == null ? void 0 : _a.getPrompt(name);
55
+ },
56
+ [prompts]
57
+ );
58
+ const getPromptsByTag = useCallback(
59
+ (tag) => {
60
+ var _a;
61
+ return ((_a = puptRef.current) == null ? void 0 : _a.getPromptsByTag(tag)) ?? [];
62
+ },
63
+ [prompts]
64
+ );
65
+ const addModule = useCallback((source) => {
66
+ setModules((prev) => {
67
+ if (prev.includes(source)) {
68
+ return prev;
69
+ }
70
+ return [...prev, source];
71
+ });
72
+ }, []);
73
+ const removeModule = useCallback((source) => {
74
+ setModules((prev) => prev.filter((m) => m !== source));
75
+ }, []);
76
+ return {
77
+ isLoading,
78
+ error,
79
+ prompts,
80
+ tags,
81
+ getPrompt,
82
+ getPromptsByTag,
83
+ addModule,
84
+ removeModule,
85
+ modules
86
+ };
87
+ }
88
+ const PuptLibraryContext = createContext(null);
89
+ PuptLibraryContext.displayName = "PuptLibraryContext";
90
+ function PuptLibraryProvider({ children, modules, searchConfig }) {
91
+ const opts = {};
92
+ if (modules) {
93
+ opts.modules = modules;
94
+ }
95
+ if (searchConfig) {
96
+ opts.searchConfig = searchConfig;
97
+ }
98
+ const library = usePuptLibrary(opts);
99
+ const value = useMemo(
100
+ () => library,
101
+ [library.isLoading, library.error, library.prompts, library.tags, library.modules]
102
+ );
103
+ return /* @__PURE__ */ jsx(PuptLibraryContext.Provider, { value, children });
104
+ }
105
+ const defaultContextValue = {
106
+ _initialized: false,
107
+ searchEngine: null,
108
+ prompts: [],
109
+ renderOptions: {},
110
+ environment: {},
111
+ isLoading: false,
112
+ error: null
113
+ };
114
+ const PuptContext = createContext(defaultContextValue);
115
+ PuptContext.displayName = "PuptContext";
116
+ async function transformSource(source, options = {}) {
117
+ const { filename = "prompt.tsx" } = options;
118
+ return createPromptFromSource(source, filename);
119
+ }
120
+ async function extractInputRequirements(element, options) {
121
+ try {
122
+ const iteratorOpts = {
123
+ validateOnSubmit: false,
124
+ environment: "browser"
125
+ };
126
+ if (options == null ? void 0 : options.values) {
127
+ iteratorOpts.values = options.values;
128
+ }
129
+ const iterator = createInputIterator(element, iteratorOpts);
130
+ const requirements = [];
131
+ await iterator.start();
132
+ while (!iterator.isDone()) {
133
+ const current = iterator.current();
134
+ if (current) {
135
+ requirements.push(current);
136
+ await iterator.submit(void 0);
137
+ }
138
+ await iterator.advance();
139
+ }
140
+ return requirements;
141
+ } catch {
142
+ return [];
143
+ }
144
+ }
145
+ function isAskComponent(type) {
146
+ if (typeof type === "string") {
147
+ return type.startsWith("Ask.") || type === "Ask";
148
+ }
149
+ return false;
150
+ }
151
+ function traverseElement(element, visitor) {
152
+ visitor(element);
153
+ const children = element[CHILDREN];
154
+ if (children) {
155
+ if (Array.isArray(children)) {
156
+ for (const child of children) {
157
+ if (isElement(child)) {
158
+ traverseElement(child, visitor);
159
+ }
160
+ }
161
+ } else if (isElement(children)) {
162
+ traverseElement(children, visitor);
163
+ }
164
+ }
165
+ }
166
+ function isElement(value) {
167
+ return isPuptElement(value);
168
+ }
169
+ function validateInput(requirement, value) {
170
+ const errors = [];
171
+ switch (requirement.type) {
172
+ case "number":
173
+ if (typeof value !== "number" || isNaN(value)) {
174
+ errors.push({
175
+ field: requirement.name,
176
+ message: `Expected a number, got ${typeof value}`,
177
+ code: "INVALID_TYPE"
178
+ });
179
+ } else {
180
+ if (requirement.min !== void 0 && value < requirement.min) {
181
+ errors.push({
182
+ field: requirement.name,
183
+ message: `Value ${value} is below minimum ${requirement.min}`,
184
+ code: "BELOW_MIN"
185
+ });
186
+ }
187
+ if (requirement.max !== void 0 && value > requirement.max) {
188
+ errors.push({
189
+ field: requirement.name,
190
+ message: `Value ${value} exceeds maximum ${requirement.max}`,
191
+ code: "ABOVE_MAX"
192
+ });
193
+ }
194
+ }
195
+ break;
196
+ case "boolean":
197
+ if (typeof value !== "boolean") {
198
+ errors.push({
199
+ field: requirement.name,
200
+ message: `Expected a boolean, got ${typeof value}`,
201
+ code: "INVALID_TYPE"
202
+ });
203
+ }
204
+ break;
205
+ case "select":
206
+ if (requirement.options && requirement.options.length > 0) {
207
+ const validValues = requirement.options.map((o) => o.value);
208
+ if (!validValues.includes(String(value))) {
209
+ errors.push({
210
+ field: requirement.name,
211
+ message: `Invalid selection. Valid options: ${validValues.join(", ")}`,
212
+ code: "INVALID_OPTION"
213
+ });
214
+ }
215
+ }
216
+ break;
217
+ case "multiselect":
218
+ if (!Array.isArray(value)) {
219
+ errors.push({
220
+ field: requirement.name,
221
+ message: "Expected an array for multiselect",
222
+ code: "INVALID_TYPE"
223
+ });
224
+ }
225
+ break;
226
+ case "string":
227
+ case "secret":
228
+ if (typeof value !== "string" && value !== void 0 && value !== null) {
229
+ errors.push({
230
+ field: requirement.name,
231
+ message: `Expected a string, got ${typeof value}`,
232
+ code: "INVALID_TYPE"
233
+ });
234
+ }
235
+ if (typeof value === "string") {
236
+ if (requirement.min !== void 0 && value.length < requirement.min) {
237
+ errors.push({
238
+ field: requirement.name,
239
+ message: `Value must be at least ${requirement.min} characters`,
240
+ code: "TOO_SHORT"
241
+ });
242
+ }
243
+ if (requirement.max !== void 0 && value.length > requirement.max) {
244
+ errors.push({
245
+ field: requirement.name,
246
+ message: `Value must be at most ${requirement.max} characters`,
247
+ code: "TOO_LONG"
248
+ });
249
+ }
250
+ }
251
+ break;
252
+ }
253
+ if (requirement.required && (value === void 0 || value === null || value === "")) {
254
+ errors.push({
255
+ field: requirement.name,
256
+ message: `${requirement.label || requirement.name} is required`,
257
+ code: "REQUIRED"
258
+ });
259
+ }
260
+ return {
261
+ valid: errors.length === 0,
262
+ errors,
263
+ warnings: []
264
+ };
265
+ }
266
+ function useAskIterator(options) {
267
+ const { element, onComplete, initialValues, preSuppliedValues, onMissingDefault } = options;
268
+ const [requirements, setRequirements] = useState([]);
269
+ const [currentIndex, setCurrentIndex] = useState(0);
270
+ const [values, setValues] = useState(() => new Map(initialValues));
271
+ const [isLoading, setIsLoading] = useState(false);
272
+ const onCompleteRef = useRef(onComplete);
273
+ onCompleteRef.current = onComplete;
274
+ const completedRef = useRef(false);
275
+ useEffect(() => {
276
+ if (!element) {
277
+ setRequirements([]);
278
+ setCurrentIndex(0);
279
+ setValues(new Map(initialValues));
280
+ completedRef.current = false;
281
+ return void 0;
282
+ }
283
+ let cancelled = false;
284
+ setIsLoading(true);
285
+ const extractOpts = preSuppliedValues ? { values: preSuppliedValues } : void 0;
286
+ void extractInputRequirements(element, extractOpts).then((reqs) => {
287
+ if (!cancelled) {
288
+ setRequirements(reqs);
289
+ setCurrentIndex(0);
290
+ setValues(new Map(initialValues));
291
+ completedRef.current = false;
292
+ setIsLoading(false);
293
+ }
294
+ });
295
+ return () => {
296
+ cancelled = true;
297
+ };
298
+ }, [element, initialValues, preSuppliedValues]);
299
+ const totalInputs = requirements.length;
300
+ const isDone = totalInputs > 0 ? currentIndex >= totalInputs : !isLoading;
301
+ const current = currentIndex < totalInputs ? requirements[currentIndex] ?? null : null;
302
+ useEffect(() => {
303
+ var _a;
304
+ if (isDone && totalInputs > 0 && !completedRef.current) {
305
+ completedRef.current = true;
306
+ (_a = onCompleteRef.current) == null ? void 0 : _a.call(onCompleteRef, new Map(values));
307
+ }
308
+ }, [isDone, totalInputs, values]);
309
+ const submit = useCallback(
310
+ (value) => {
311
+ if (!current) {
312
+ return Promise.resolve({
313
+ valid: false,
314
+ errors: [{ field: "", message: "No current requirement", code: "NO_REQUIREMENT" }],
315
+ warnings: []
316
+ });
317
+ }
318
+ const result = validateInput(current, value);
319
+ if (result.valid) {
320
+ setValues((prev) => {
321
+ const next = new Map(prev);
322
+ next.set(current.name, value);
323
+ return next;
324
+ });
325
+ setCurrentIndex((prev) => prev + 1);
326
+ }
327
+ return Promise.resolve(result);
328
+ },
329
+ [current]
330
+ );
331
+ const previous = useCallback(() => {
332
+ setCurrentIndex((prev) => Math.max(0, prev - 1));
333
+ completedRef.current = false;
334
+ }, []);
335
+ const goTo = useCallback(
336
+ (index) => {
337
+ if (index >= 0 && index < totalInputs) {
338
+ setCurrentIndex(index);
339
+ completedRef.current = false;
340
+ }
341
+ },
342
+ [totalInputs]
343
+ );
344
+ const reset = useCallback(() => {
345
+ setCurrentIndex(0);
346
+ setValues(/* @__PURE__ */ new Map());
347
+ completedRef.current = false;
348
+ }, []);
349
+ const setValue = useCallback((name, value) => {
350
+ setValues((prev) => {
351
+ const next = new Map(prev);
352
+ next.set(name, value);
353
+ return next;
354
+ });
355
+ }, []);
356
+ const getValue = useCallback(
357
+ (name) => {
358
+ return values.get(name);
359
+ },
360
+ [values]
361
+ );
362
+ const runNonInteractive = useCallback(async () => {
363
+ if (!element) {
364
+ return /* @__PURE__ */ new Map();
365
+ }
366
+ const iteratorOpts = {
367
+ validateOnSubmit: false,
368
+ environment: "browser",
369
+ nonInteractive: true,
370
+ onMissingDefault: onMissingDefault ?? "error"
371
+ };
372
+ if (preSuppliedValues) {
373
+ iteratorOpts.values = preSuppliedValues;
374
+ }
375
+ const iterator = createInputIterator(element, iteratorOpts);
376
+ await iterator.start();
377
+ const result = await iterator.runNonInteractive();
378
+ setValues(result);
379
+ setCurrentIndex(requirements.length);
380
+ return result;
381
+ }, [element, preSuppliedValues, onMissingDefault, requirements.length]);
382
+ return {
383
+ requirements,
384
+ current,
385
+ currentIndex,
386
+ totalInputs,
387
+ isDone,
388
+ isLoading,
389
+ inputs: values,
390
+ submit,
391
+ previous,
392
+ goTo,
393
+ reset,
394
+ setValue,
395
+ getValue,
396
+ runNonInteractive
397
+ };
398
+ }
399
+ const INPUT_TYPE_MAP = {
400
+ string: "text",
401
+ number: "number",
402
+ boolean: "checkbox",
403
+ secret: "password",
404
+ date: "date",
405
+ path: "text",
406
+ select: "text",
407
+ multiselect: "text",
408
+ rating: "number"
409
+ };
410
+ function AskHandler({ children, element, onComplete, initialValues }) {
411
+ const {
412
+ requirements,
413
+ current,
414
+ currentIndex,
415
+ totalInputs,
416
+ isDone,
417
+ isLoading,
418
+ inputs: values,
419
+ submit,
420
+ previous,
421
+ goTo,
422
+ reset,
423
+ setValue,
424
+ getValue
425
+ } = useAskIterator({
426
+ element,
427
+ ...onComplete !== void 0 && { onComplete },
428
+ ...initialValues !== void 0 && { initialValues }
429
+ });
430
+ let progress;
431
+ if (totalInputs === 0) {
432
+ progress = isDone ? 100 : 0;
433
+ } else {
434
+ progress = Math.round(currentIndex / totalInputs * 100);
435
+ }
436
+ const getInputProps = useCallback(
437
+ (name) => {
438
+ const requirement = requirements.find((r) => r.name === name) ?? {
439
+ name,
440
+ label: name,
441
+ type: "string",
442
+ description: name,
443
+ required: false
444
+ };
445
+ const inputType = INPUT_TYPE_MAP[requirement.type] ?? "text";
446
+ return {
447
+ inputProps: {
448
+ id: `ask-${name}`,
449
+ name,
450
+ type: inputType,
451
+ required: requirement.required ?? false,
452
+ "aria-label": requirement.label
453
+ },
454
+ requirement,
455
+ value: getValue(name),
456
+ setValue: (value) => {
457
+ setValue(name, value);
458
+ },
459
+ errors: []
460
+ };
461
+ },
462
+ [requirements, getValue, setValue]
463
+ );
464
+ return /* @__PURE__ */ jsx(Fragment, { children: children({
465
+ requirements,
466
+ current,
467
+ currentIndex,
468
+ totalInputs,
469
+ progress,
470
+ isDone,
471
+ isLoading,
472
+ values,
473
+ submit,
474
+ previous,
475
+ goTo,
476
+ reset,
477
+ getInputProps
478
+ }) });
479
+ }
480
+ const DEFAULT_DEBOUNCE$1 = 300;
481
+ function PromptEditor({
482
+ children,
483
+ defaultValue = "",
484
+ onChange,
485
+ debounce = DEFAULT_DEBOUNCE$1
486
+ }) {
487
+ const [value, setValueState] = useState(defaultValue);
488
+ const [element, setElement] = useState(null);
489
+ const [error, setError] = useState(null);
490
+ const [isTransforming, setIsTransforming] = useState(false);
491
+ const timerRef = useRef(null);
492
+ const mountedRef = useRef(true);
493
+ useEffect(() => {
494
+ mountedRef.current = true;
495
+ return () => {
496
+ mountedRef.current = false;
497
+ if (timerRef.current !== null) {
498
+ clearTimeout(timerRef.current);
499
+ }
500
+ };
501
+ }, []);
502
+ const scheduleTransform = useCallback(
503
+ (source) => {
504
+ if (timerRef.current !== null) {
505
+ clearTimeout(timerRef.current);
506
+ timerRef.current = null;
507
+ }
508
+ if (source.trim() === "") {
509
+ setElement(null);
510
+ setError(null);
511
+ setIsTransforming(false);
512
+ return;
513
+ }
514
+ const doTransform = async () => {
515
+ timerRef.current = null;
516
+ if (!mountedRef.current) {
517
+ return;
518
+ }
519
+ setIsTransforming(true);
520
+ try {
521
+ const result = await transformSource(source);
522
+ if (mountedRef.current) {
523
+ setElement(result);
524
+ setError(null);
525
+ }
526
+ } catch (err) {
527
+ if (mountedRef.current) {
528
+ setElement(null);
529
+ setError(err instanceof Error ? err : new Error(String(err)));
530
+ }
531
+ } finally {
532
+ if (mountedRef.current) {
533
+ setIsTransforming(false);
534
+ }
535
+ }
536
+ };
537
+ if (debounce <= 0) {
538
+ void doTransform();
539
+ } else {
540
+ setIsTransforming(true);
541
+ timerRef.current = setTimeout(() => {
542
+ void doTransform();
543
+ }, debounce);
544
+ }
545
+ },
546
+ [debounce]
547
+ );
548
+ useEffect(() => {
549
+ if (defaultValue.trim() !== "") {
550
+ scheduleTransform(defaultValue);
551
+ }
552
+ }, []);
553
+ const handleChange = useCallback(
554
+ (e) => {
555
+ const newValue = e.target.value;
556
+ setValueState(newValue);
557
+ onChange == null ? void 0 : onChange(newValue);
558
+ scheduleTransform(newValue);
559
+ },
560
+ [onChange, scheduleTransform]
561
+ );
562
+ const setValue = useCallback(
563
+ (newValue) => {
564
+ setValueState(newValue);
565
+ onChange == null ? void 0 : onChange(newValue);
566
+ scheduleTransform(newValue);
567
+ },
568
+ [onChange, scheduleTransform]
569
+ );
570
+ return /* @__PURE__ */ jsx(Fragment, { children: children({
571
+ inputProps: {
572
+ value,
573
+ onChange: handleChange
574
+ },
575
+ value,
576
+ setValue,
577
+ element,
578
+ error,
579
+ isTransforming
580
+ }) });
581
+ }
582
+ function usePupt() {
583
+ const context = useContext(PuptContext);
584
+ if (!context._initialized) {
585
+ throw new Error("usePupt must be used within a PuptProvider");
586
+ }
587
+ return context;
588
+ }
589
+ function usePromptRender(options = {}) {
590
+ const {
591
+ source: initialSource,
592
+ inputs,
593
+ environment,
594
+ renderOptions: customRenderOptions,
595
+ autoRender = false,
596
+ filename
597
+ } = options;
598
+ const { renderOptions: contextRenderOptions, environment: contextEnvironment } = usePupt();
599
+ const [source, setSourceState] = useState(initialSource ?? null);
600
+ const [element, setElement] = useState(null);
601
+ const [output, setOutput] = useState(null);
602
+ const [error, setError] = useState(null);
603
+ const [isTransforming, setIsTransforming] = useState(false);
604
+ const [isRendering, setIsRendering] = useState(false);
605
+ const [inputRequirements, setInputRequirements] = useState([]);
606
+ const [postActions, setPostActions] = useState([]);
607
+ const [renderErrors, setRenderErrors] = useState([]);
608
+ const mountedRef = useRef(true);
609
+ useEffect(() => {
610
+ mountedRef.current = true;
611
+ return () => {
612
+ mountedRef.current = false;
613
+ };
614
+ }, []);
615
+ const doTransform = useCallback(
616
+ async (sourceToTransform) => {
617
+ setIsTransforming(true);
618
+ setError(null);
619
+ try {
620
+ let newElement;
621
+ if (sourceToTransform.type === "element") {
622
+ newElement = sourceToTransform.value;
623
+ } else {
624
+ newElement = await transformSource(sourceToTransform.value, filename ? { filename } : void 0);
625
+ }
626
+ if (mountedRef.current) {
627
+ setElement(newElement);
628
+ const requirements = await extractInputRequirements(newElement);
629
+ setInputRequirements(requirements);
630
+ }
631
+ return newElement;
632
+ } catch (err) {
633
+ if (mountedRef.current) {
634
+ setError(err instanceof Error ? err : new Error(String(err)));
635
+ setElement(null);
636
+ setInputRequirements([]);
637
+ }
638
+ return null;
639
+ } finally {
640
+ if (mountedRef.current) {
641
+ setIsTransforming(false);
642
+ }
643
+ }
644
+ },
645
+ [filename]
646
+ );
647
+ const doRender = useCallback(
648
+ async (elementToRender) => {
649
+ setIsRendering(true);
650
+ setError(null);
651
+ setRenderErrors([]);
652
+ try {
653
+ const mergedOptions = {
654
+ ...contextRenderOptions,
655
+ ...customRenderOptions,
656
+ inputs: inputs ?? /* @__PURE__ */ new Map()
657
+ };
658
+ const envOverrides = { ...contextEnvironment, ...environment };
659
+ if (Object.keys(envOverrides).length > 0) {
660
+ mergedOptions.env = createEnvironment(envOverrides);
661
+ }
662
+ const result = await render(elementToRender, mergedOptions);
663
+ if (mountedRef.current) {
664
+ setOutput(result.text);
665
+ setPostActions(result.postExecution ?? []);
666
+ if (!result.ok && "errors" in result) {
667
+ setRenderErrors(result.errors);
668
+ const firstError = result.errors[0];
669
+ if (firstError) {
670
+ setError(new Error(`${firstError.component}: ${firstError.message}`));
671
+ }
672
+ } else {
673
+ setRenderErrors([]);
674
+ }
675
+ }
676
+ } catch (err) {
677
+ if (mountedRef.current) {
678
+ setError(err instanceof Error ? err : new Error(String(err)));
679
+ setOutput(null);
680
+ setPostActions([]);
681
+ }
682
+ } finally {
683
+ if (mountedRef.current) {
684
+ setIsRendering(false);
685
+ }
686
+ }
687
+ },
688
+ [contextRenderOptions, contextEnvironment, customRenderOptions, environment, inputs]
689
+ );
690
+ const transform = useCallback(
691
+ async (sourceCode) => {
692
+ const sourceToUse = sourceCode ? { type: "source", value: sourceCode } : source;
693
+ if (!sourceToUse) {
694
+ return null;
695
+ }
696
+ if (sourceCode) {
697
+ setSourceState({ type: "source", value: sourceCode });
698
+ }
699
+ return doTransform(sourceToUse);
700
+ },
701
+ [source, doTransform]
702
+ );
703
+ const render$1 = useCallback(async () => {
704
+ if (element) {
705
+ await doRender(element);
706
+ }
707
+ }, [element, doRender]);
708
+ const setSource = useCallback((newSource) => {
709
+ setSourceState(newSource);
710
+ }, []);
711
+ const externalSourceValue = (initialSource == null ? void 0 : initialSource.type) === "source" ? initialSource.value : null;
712
+ const prevExternalSourceValue = useRef(externalSourceValue);
713
+ useEffect(() => {
714
+ if (externalSourceValue !== prevExternalSourceValue.current) {
715
+ prevExternalSourceValue.current = externalSourceValue;
716
+ if (initialSource) {
717
+ setSourceState(initialSource);
718
+ }
719
+ }
720
+ }, [externalSourceValue, initialSource]);
721
+ useEffect(() => {
722
+ if (source) {
723
+ void doTransform(source);
724
+ } else {
725
+ setElement(null);
726
+ setInputRequirements([]);
727
+ setError(null);
728
+ }
729
+ }, [source, doTransform]);
730
+ useEffect(() => {
731
+ if (autoRender && element) {
732
+ void doRender(element);
733
+ }
734
+ }, [autoRender, element, doRender]);
735
+ useEffect(() => {
736
+ if (autoRender && element && inputs) {
737
+ void doRender(element);
738
+ }
739
+ }, [autoRender, element, inputs, doRender]);
740
+ return {
741
+ source,
742
+ setSource,
743
+ element,
744
+ output,
745
+ error,
746
+ renderErrors,
747
+ isTransforming,
748
+ isRendering,
749
+ isLoading: isTransforming || isRendering,
750
+ inputRequirements,
751
+ postActions,
752
+ render: render$1,
753
+ transform
754
+ };
755
+ }
756
+ function PromptRenderer({
757
+ children,
758
+ source,
759
+ autoRender = true,
760
+ inputs,
761
+ renderOptions,
762
+ environment,
763
+ filename
764
+ }) {
765
+ const [isCopied, setIsCopied] = useState(false);
766
+ const normalizedSource = useMemo(
767
+ () => typeof source === "string" ? { type: "source", value: source } : source,
768
+ [source]
769
+ );
770
+ const {
771
+ output,
772
+ error,
773
+ isLoading,
774
+ inputRequirements,
775
+ postActions,
776
+ render: doRender
777
+ } = usePromptRender({
778
+ source: normalizedSource,
779
+ autoRender,
780
+ ...inputs !== void 0 && { inputs },
781
+ ...renderOptions !== void 0 && { renderOptions },
782
+ ...environment !== void 0 && { environment },
783
+ ...filename !== void 0 && { filename }
784
+ });
785
+ const isReady = output !== null && inputRequirements.length === 0;
786
+ const copyToClipboard = useCallback(async () => {
787
+ if (output === null) {
788
+ return;
789
+ }
790
+ try {
791
+ await navigator.clipboard.writeText(output);
792
+ setIsCopied(true);
793
+ setTimeout(() => {
794
+ setIsCopied(false);
795
+ }, 2e3);
796
+ } catch {
797
+ }
798
+ }, [output]);
799
+ const render2 = useCallback(async () => {
800
+ await doRender();
801
+ }, [doRender]);
802
+ return /* @__PURE__ */ jsx(Fragment, { children: children({
803
+ output,
804
+ isReady,
805
+ isLoading,
806
+ error,
807
+ pendingInputs: inputRequirements,
808
+ postActions,
809
+ copyToClipboard,
810
+ isCopied,
811
+ render: render2
812
+ }) });
813
+ }
814
+ function PuptProvider({
815
+ children,
816
+ prompts,
817
+ searchConfig,
818
+ renderOptions = {},
819
+ environment = {}
820
+ }) {
821
+ const [isLoading] = useState(false);
822
+ const [error] = useState(null);
823
+ const searchEngine = useMemo(() => {
824
+ if (!prompts || prompts.length === 0) {
825
+ return null;
826
+ }
827
+ try {
828
+ const engine = createSearchEngine(searchConfig);
829
+ engine.index(prompts);
830
+ return engine;
831
+ } catch (err) {
832
+ console.error("Failed to initialize search engine:", err);
833
+ return null;
834
+ }
835
+ }, [prompts, searchConfig]);
836
+ const contextValue = useMemo(
837
+ () => ({
838
+ _initialized: true,
839
+ searchEngine,
840
+ prompts: prompts ?? [],
841
+ renderOptions,
842
+ environment,
843
+ isLoading,
844
+ error
845
+ }),
846
+ [searchEngine, prompts, renderOptions, environment, isLoading, error]
847
+ );
848
+ return /* @__PURE__ */ jsx(PuptContext.Provider, { value: contextValue, children });
849
+ }
850
+ function useFormula(options) {
851
+ const { formula, inputs } = options;
852
+ return useMemo(() => {
853
+ try {
854
+ return { result: evaluateFormula(formula, inputs), error: null };
855
+ } catch (err) {
856
+ return {
857
+ result: false,
858
+ error: err instanceof Error ? err : new Error(String(err))
859
+ };
860
+ }
861
+ }, [formula, inputs]);
862
+ }
863
+ function usePostActions(options) {
864
+ const { actions, handlers } = options;
865
+ const [executedSet, setExecutedSet] = useState(() => /* @__PURE__ */ new Set());
866
+ const [dismissedSet, setDismissedSet] = useState(() => /* @__PURE__ */ new Set());
867
+ const prevActionsRef = useRef(actions);
868
+ if (prevActionsRef.current !== actions) {
869
+ prevActionsRef.current = actions;
870
+ setExecutedSet(/* @__PURE__ */ new Set());
871
+ setDismissedSet(/* @__PURE__ */ new Set());
872
+ }
873
+ const pendingActions = actions.filter((a) => !executedSet.has(a) && !dismissedSet.has(a));
874
+ const executedActions = actions.filter((a) => executedSet.has(a));
875
+ const dismissedActions = actions.filter((a) => dismissedSet.has(a));
876
+ const allDone = pendingActions.length === 0;
877
+ const execute = useCallback(
878
+ async (action) => {
879
+ if (executedSet.has(action)) {
880
+ return;
881
+ }
882
+ const handler = handlers == null ? void 0 : handlers[action.type];
883
+ if (handler) {
884
+ await handler(action);
885
+ }
886
+ setExecutedSet((prev) => {
887
+ const next = new Set(prev);
888
+ next.add(action);
889
+ return next;
890
+ });
891
+ },
892
+ [handlers, executedSet]
893
+ );
894
+ const dismiss = useCallback((action) => {
895
+ setDismissedSet((prev) => {
896
+ const next = new Set(prev);
897
+ next.add(action);
898
+ return next;
899
+ });
900
+ }, []);
901
+ const executeAll = useCallback(async () => {
902
+ for (const action of pendingActions) {
903
+ const handler = handlers == null ? void 0 : handlers[action.type];
904
+ if (handler) {
905
+ await handler(action);
906
+ }
907
+ }
908
+ setExecutedSet((prev) => {
909
+ const next = new Set(prev);
910
+ for (const action of pendingActions) {
911
+ next.add(action);
912
+ }
913
+ return next;
914
+ });
915
+ }, [pendingActions, handlers]);
916
+ const dismissAll = useCallback(() => {
917
+ setDismissedSet((prev) => {
918
+ const next = new Set(prev);
919
+ for (const action of pendingActions) {
920
+ next.add(action);
921
+ }
922
+ return next;
923
+ });
924
+ }, [pendingActions]);
925
+ const reset = useCallback(() => {
926
+ setExecutedSet(/* @__PURE__ */ new Set());
927
+ setDismissedSet(/* @__PURE__ */ new Set());
928
+ }, []);
929
+ return {
930
+ pendingActions,
931
+ executedActions,
932
+ dismissedActions,
933
+ allDone,
934
+ execute,
935
+ dismiss,
936
+ executeAll,
937
+ dismissAll,
938
+ reset
939
+ };
940
+ }
941
+ const DEFAULT_DEBOUNCE = 200;
942
+ function usePromptSearch(options = {}) {
943
+ const { debounce = DEFAULT_DEBOUNCE, limit } = options;
944
+ const { searchEngine } = usePupt();
945
+ const [query, setQueryState] = useState("");
946
+ const [results, setResults] = useState([]);
947
+ const [isSearching, setIsSearching] = useState(false);
948
+ const timerRef = useRef(null);
949
+ const performSearch = useCallback(
950
+ (searchQuery) => {
951
+ if (!searchEngine || searchQuery.trim() === "") {
952
+ setResults([]);
953
+ setIsSearching(false);
954
+ return;
955
+ }
956
+ setIsSearching(true);
957
+ try {
958
+ const searchOptions = limit !== void 0 ? { limit } : {};
959
+ const searchResults = searchEngine.search(searchQuery, searchOptions);
960
+ setResults(searchResults);
961
+ } finally {
962
+ setIsSearching(false);
963
+ }
964
+ },
965
+ [searchEngine, limit]
966
+ );
967
+ const setQuery = useCallback(
968
+ (newQuery) => {
969
+ setQueryState(newQuery);
970
+ if (timerRef.current !== null) {
971
+ clearTimeout(timerRef.current);
972
+ timerRef.current = null;
973
+ }
974
+ if (newQuery.trim() === "") {
975
+ setResults([]);
976
+ setIsSearching(false);
977
+ return;
978
+ }
979
+ if (debounce <= 0) {
980
+ performSearch(newQuery);
981
+ } else {
982
+ setIsSearching(true);
983
+ timerRef.current = setTimeout(() => {
984
+ timerRef.current = null;
985
+ performSearch(newQuery);
986
+ }, debounce);
987
+ }
988
+ },
989
+ [debounce, performSearch]
990
+ );
991
+ const clear = useCallback(() => {
992
+ if (timerRef.current !== null) {
993
+ clearTimeout(timerRef.current);
994
+ timerRef.current = null;
995
+ }
996
+ setQueryState("");
997
+ setResults([]);
998
+ setIsSearching(false);
999
+ }, []);
1000
+ const allTags = searchEngine ? searchEngine.getAllTags() : [];
1001
+ useEffect(() => {
1002
+ return () => {
1003
+ if (timerRef.current !== null) {
1004
+ clearTimeout(timerRef.current);
1005
+ }
1006
+ };
1007
+ }, []);
1008
+ return {
1009
+ query,
1010
+ setQuery,
1011
+ results,
1012
+ isSearching,
1013
+ allTags,
1014
+ clear
1015
+ };
1016
+ }
1017
+ function usePuptLibraryContext() {
1018
+ const ctx = useContext(PuptLibraryContext);
1019
+ if (!ctx) {
1020
+ throw new Error("usePuptLibraryContext must be used within a PuptLibraryProvider");
1021
+ }
1022
+ return ctx;
1023
+ }
1024
+ const VERSION = "1.0.0";
1025
+ export {
1026
+ AskHandler,
1027
+ CHILDREN2 as CHILDREN,
1028
+ COMPONENT_MARKER,
1029
+ Component,
1030
+ DEFERRED_REF,
1031
+ PROPS,
1032
+ PromptEditor,
1033
+ PromptRenderer,
1034
+ PuptContext,
1035
+ PuptLibraryContext,
1036
+ PuptLibraryProvider,
1037
+ PuptProvider,
1038
+ TYPE,
1039
+ VERSION,
1040
+ createRuntimeConfig,
1041
+ evaluateFormula2 as evaluateFormula,
1042
+ extractInputRequirements,
1043
+ isAskComponent,
1044
+ isComponentClass,
1045
+ isDeferredRef,
1046
+ isElement,
1047
+ isPuptElement2 as isPuptElement,
1048
+ transformSource,
1049
+ traverseElement,
1050
+ useAskIterator,
1051
+ useFormula,
1052
+ usePostActions,
1053
+ usePromptRender,
1054
+ usePromptSearch,
1055
+ usePupt,
1056
+ usePuptLibrary,
1057
+ usePuptLibraryContext,
1058
+ validateInput
1059
+ };
1060
+ //# sourceMappingURL=index.js.map