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