@app-studio/web 0.9.65 → 0.9.67

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.
@@ -2,11 +2,35 @@
2
2
 
3
3
  A customizable chat input component with file upload support, mentions, suggestions, audio recording, and model selection.
4
4
 
5
- ### **Import**
5
+ ## **Import**
6
6
  ```tsx
7
7
  import { ChatInput } from '@app-studio/web';
8
8
  ```
9
9
 
10
+ ## **Overview**
11
+
12
+ The ChatInput component is a feature-rich, production-ready chat input with comprehensive file handling capabilities. It provides:
13
+
14
+ - **File Upload Management**: Support for single and multiple file uploads with drag-and-drop
15
+ - **Progress Tracking**: Real-time upload progress monitoring
16
+ - **File Validation**: Built-in file size validation (50MB limit)
17
+ - **Audio Recording**: Optional audio recording with automatic file conversion
18
+ - **Mentions & Suggestions**: Auto-completion for mentions and suggestions
19
+ - **Agent Integration**: Support for running agents with stop functionality
20
+ - **Customizable Styling**: Flexible theming through the `views` prop
21
+ - **Accessibility**: Keyboard shortcuts and screen reader support
22
+
23
+ ### **Key Features**
24
+
25
+ 1. **Automatic File Queue Management**: Files are automatically queued and uploaded sequentially
26
+ 2. **Drag-and-Drop Support**: Built-in drag-and-drop with visual feedback
27
+ 3. **File Attachments Display**: Uploaded files are displayed as removable attachments
28
+ 4. **Error Handling**: Comprehensive error handling with customizable error messages
29
+ 5. **Model Selection**: Support for AI model selection with thinking mode
30
+ 6. **Prompt Examples**: Display clickable prompt examples for quick input
31
+
32
+ ## **Basic Usage**
33
+
10
34
  ### **Default**
11
35
  ```tsx
12
36
  import React, { useState } from 'react';
@@ -25,6 +49,44 @@ export const DefaultChatInput = () => {
25
49
  };
26
50
  ```
27
51
 
52
+ ## **Required Props**
53
+
54
+ ### **getPendingFiles**
55
+ Function to retrieve the current list of pending files. This is required for the component to access files that are staged for upload.
56
+
57
+ - **Type:** `() => File[]`
58
+ - **Required:** `true`
59
+
60
+ ### **clearPendingFiles**
61
+ Function to clear all pending files. This is typically called after a successful submission or when files need to be reset.
62
+
63
+ - **Type:** `() => void`
64
+ - **Required:** `true`
65
+
66
+ ```tsx
67
+ import React, { useState } from 'react';
68
+ import { ChatInput } from '@app-studio/web';
69
+
70
+ export const FileManagementExample = () => {
71
+ const [pendingFiles, setPendingFiles] = useState<File[]>([]);
72
+
73
+ return (
74
+ <ChatInput
75
+ onSubmit={(message) => {
76
+ console.log('Message:', message);
77
+ console.log('Files:', pendingFiles);
78
+ // Clear files after submission
79
+ setPendingFiles([]);
80
+ }}
81
+ getPendingFiles={() => pendingFiles}
82
+ clearPendingFiles={() => setPendingFiles([])}
83
+ />
84
+ );
85
+ };
86
+ ```
87
+
88
+ ## **Core Props**
89
+
28
90
  ### **onSubmit**
29
91
  Callback function when the form is submitted.
30
92
 
@@ -289,6 +351,247 @@ export const UploadErrorChatInput = () => {
289
351
  };
290
352
  ```
291
353
 
354
+ ## **File Upload Management**
355
+
356
+ The ChatInput component provides comprehensive file upload capabilities with support for drag-and-drop, multiple file selection, progress tracking, and file validation. Here's how to properly manage file uploads:
357
+
358
+ ### **Complete File Upload Example**
359
+
360
+ This example demonstrates the complete file upload workflow with all features:
361
+
362
+ ```tsx
363
+ import React, { useState } from 'react';
364
+ import { ChatInput } from '@app-studio/web';
365
+ import { UploadService } from 'src/services/api';
366
+
367
+ export const CompleteFileUploadExample = () => {
368
+ const [files, setFiles] = useState<File[]>([]);
369
+ const [uploadProgress, setUploadProgress] = useState(0);
370
+ const [error, setError] = useState<string>('');
371
+
372
+ const uploadFileRequest = UploadService.useUploadControllerFileService({
373
+ onProgress: (progress) => {
374
+ setUploadProgress(progress);
375
+ console.log(`Upload progress: ${progress}%`);
376
+ },
377
+ onSuccess: (data) => {
378
+ console.log('Upload successful:', data);
379
+ setUploadProgress(0);
380
+ setError('');
381
+ },
382
+ onError: (err) => {
383
+ console.error('Upload failed:', err);
384
+ setError('Failed to upload file. Please try again.');
385
+ setUploadProgress(0);
386
+ },
387
+ });
388
+
389
+ return (
390
+ <>
391
+ {error && <div style={{ color: 'red', marginBottom: 8 }}>{error}</div>}
392
+ {uploadProgress > 0 && (
393
+ <div style={{ marginBottom: 8 }}>
394
+ Upload Progress: {uploadProgress}%
395
+ </div>
396
+ )}
397
+
398
+ <ChatInput
399
+ onFileUpload={(file) => uploadFileRequest.run({ file })}
400
+ onUploadProgress={setUploadProgress}
401
+ onUploadSuccess={(data) => {
402
+ console.log('File uploaded:', data);
403
+ setError('');
404
+ }}
405
+ onUploadError={(err) => {
406
+ setError(err.message || 'Upload failed');
407
+ }}
408
+ onSubmit={(message) => {
409
+ console.log('Message:', message);
410
+ console.log('Attached files:', files);
411
+ setFiles([]);
412
+ }}
413
+ getPendingFiles={() => files}
414
+ clearPendingFiles={() => setFiles([])}
415
+ />
416
+ </>
417
+ );
418
+ };
419
+ ```
420
+
421
+ ### **Drag and Drop Support**
422
+
423
+ The ChatInput component automatically supports drag-and-drop file uploads. Simply drag files over the input area:
424
+
425
+ ```tsx
426
+ import React, { useState } from 'react';
427
+ import { ChatInput } from '@app-studio/web';
428
+
429
+ export const DragDropExample = () => {
430
+ const [files, setFiles] = useState<File[]>([]);
431
+
432
+ return (
433
+ <ChatInput
434
+ onFileUpload={(file) => {
435
+ console.log('File dropped:', file.name);
436
+ // Handle file upload
437
+ }}
438
+ onSubmit={(message) => console.log(message)}
439
+ getPendingFiles={() => files}
440
+ clearPendingFiles={() => setFiles([])}
441
+ // The component automatically handles drag-over visual feedback
442
+ />
443
+ );
444
+ };
445
+ ```
446
+
447
+ ### **Multiple File Upload**
448
+
449
+ The ChatInput supports uploading multiple files at once. Files are processed sequentially:
450
+
451
+ ```tsx
452
+ import React, { useState } from 'react';
453
+ import { ChatInput } from '@app-studio/web';
454
+
455
+ export const MultipleFilesExample = () => {
456
+ const [files, setFiles] = useState<File[]>([]);
457
+ const [uploadedFiles, setUploadedFiles] = useState<File[]>([]);
458
+
459
+ const handleFileUpload = (file: File) => {
460
+ console.log('Uploading file:', file.name);
461
+
462
+ // Simulate upload
463
+ setTimeout(() => {
464
+ setUploadedFiles(prev => [...prev, file]);
465
+ console.log('File uploaded successfully:', file.name);
466
+ }, 1000);
467
+ };
468
+
469
+ return (
470
+ <div>
471
+ <div style={{ marginBottom: 8 }}>
472
+ Uploaded files: {uploadedFiles.length}
473
+ </div>
474
+
475
+ <ChatInput
476
+ onFileUpload={handleFileUpload}
477
+ onSubmit={(message) => {
478
+ console.log('Message:', message);
479
+ console.log('All files:', uploadedFiles);
480
+ setUploadedFiles([]);
481
+ }}
482
+ getPendingFiles={() => files}
483
+ clearPendingFiles={() => setFiles([])}
484
+ />
485
+ </div>
486
+ );
487
+ };
488
+ ```
489
+
490
+ ### **File Size Validation**
491
+
492
+ The component includes built-in file size validation (50MB limit by default):
493
+
494
+ ```tsx
495
+ import React, { useState } from 'react';
496
+ import { ChatInput } from '@app-studio/web';
497
+
498
+ export const FileSizeValidationExample = () => {
499
+ const [files, setFiles] = useState<File[]>([]);
500
+ const [error, setError] = useState<string>('');
501
+
502
+ const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
503
+
504
+ return (
505
+ <>
506
+ {error && <div style={{ color: 'red' }}>{error}</div>}
507
+
508
+ <ChatInput
509
+ onFileUpload={(file) => {
510
+ if (file.size > MAX_FILE_SIZE) {
511
+ setError(`File "${file.name}" exceeds 50MB limit`);
512
+ return;
513
+ }
514
+
515
+ setError('');
516
+ console.log('Uploading file:', file.name);
517
+ // Proceed with upload
518
+ }}
519
+ onUploadError={(err) => {
520
+ setError('Upload failed: ' + err.message);
521
+ }}
522
+ onSubmit={(message) => console.log(message)}
523
+ getPendingFiles={() => files}
524
+ clearPendingFiles={() => setFiles([])}
525
+ />
526
+ </>
527
+ );
528
+ };
529
+ ```
530
+
531
+ ### **File Upload Lifecycle**
532
+
533
+ Understanding the file upload lifecycle is crucial for proper implementation:
534
+
535
+ 1. **File Selection**: User selects files via button click or drag-and-drop
536
+ 2. **Validation**: Files are validated for size (50MB limit)
537
+ 3. **Queue**: Valid files are added to the upload queue
538
+ 4. **Upload**: Files are uploaded sequentially using `onFileUpload`
539
+ 5. **Progress**: `onUploadProgress` is called with progress updates (0-100)
540
+ 6. **Success/Error**: `onUploadSuccess` or `onUploadError` is called
541
+ 7. **Display**: Uploaded files are displayed as attachments in the input
542
+ 8. **Submission**: Files are included when the message is submitted
543
+ 9. **Cleanup**: Files are cleared using `clearPendingFiles`
544
+
545
+ ```tsx
546
+ import React, { useState } from 'react';
547
+ import { ChatInput } from '@app-studio/web';
548
+
549
+ export const FileLifecycleExample = () => {
550
+ const [files, setFiles] = useState<File[]>([]);
551
+ const [uploadStatus, setUploadStatus] = useState<string>('idle');
552
+
553
+ return (
554
+ <>
555
+ <div>Status: {uploadStatus}</div>
556
+
557
+ <ChatInput
558
+ onFileUpload={(file) => {
559
+ setUploadStatus(`Uploading ${file.name}...`);
560
+ // Upload logic here
561
+ }}
562
+ onUploadProgress={(progress) => {
563
+ setUploadStatus(`Upload progress: ${progress}%`);
564
+ }}
565
+ onUploadSuccess={(data) => {
566
+ setUploadStatus('Upload complete!');
567
+ setTimeout(() => setUploadStatus('idle'), 2000);
568
+ }}
569
+ onUploadError={(error) => {
570
+ setUploadStatus(`Upload failed: ${error.message}`);
571
+ }}
572
+ onSubmit={(message) => {
573
+ console.log('Submitting with files:', files);
574
+ setFiles([]);
575
+ setUploadStatus('idle');
576
+ }}
577
+ getPendingFiles={() => files}
578
+ clearPendingFiles={() => {
579
+ setFiles([]);
580
+ setUploadStatus('idle');
581
+ }}
582
+ />
583
+ </>
584
+ );
585
+ };
586
+ ```
587
+
588
+ ### **Removing Uploaded Files**
589
+
590
+ Users can remove files before submission by clicking the remove button on each file attachment. The component handles this automatically.
591
+
592
+ ## **Other Props**
593
+
594
+
292
595
  ### **hideAttachments**
293
596
  Whether to hide the attachment button.
294
597
 
@@ -313,24 +616,78 @@ export const NoAttachmentsChatInput = () => {
313
616
  };
314
617
  ```
315
618
 
316
- ### **enableAudioRecording**
317
- Enable audio recording button.
619
+
620
+ ### **sandboxId**
621
+ ID of the sandbox environment for file uploads. Used when integrating with sandbox-based file systems.
622
+
623
+ - **Type:** `string`
624
+ - **Required:** `false`
625
+
626
+ ```tsx
627
+ import React, { useState } from 'react';
628
+ import { ChatInput } from '@app-studio/web';
629
+
630
+ export const SandboxChatInput = () => {
631
+ const [files, setFiles] = useState<File[]>([]);
632
+
633
+ return (
634
+ <ChatInput
635
+ sandboxId="my-sandbox-123"
636
+ onFileUpload={(file) => {
637
+ console.log('Uploading to sandbox:', file.name);
638
+ // File will be uploaded to the specified sandbox
639
+ }}
640
+ onSubmit={(message) => console.log(message)}
641
+ getPendingFiles={() => files}
642
+ clearPendingFiles={() => setFiles([])}
643
+ />
644
+ );
645
+ };
646
+ ```
647
+
648
+ ### **onFileBrowse**
649
+ Callback function when the file browser is opened. Useful for tracking user interactions.
650
+
651
+ - **Type:** `() => void`
652
+ - **Required:** `false`
653
+
654
+ ```tsx
655
+ import React, { useState } from 'react';
656
+ import { ChatInput } from '@app-studio/web';
657
+
658
+ export const FileBrowseChatInput = () => {
659
+ const [files, setFiles] = useState<File[]>([]);
660
+
661
+ return (
662
+ <ChatInput
663
+ onFileBrowse={() => {
664
+ console.log('User opened file browser');
665
+ // Track analytics or perform other actions
666
+ }}
667
+ onSubmit={(message) => console.log(message)}
668
+ getPendingFiles={() => files}
669
+ clearPendingFiles={() => setFiles([])}
670
+ />
671
+ );
672
+ };
673
+ ```
674
+
675
+ ### **autoFocus**
676
+ Whether to automatically focus the input on mount.
318
677
 
319
678
  - **Type:** `boolean`
320
- - **Default:** `false`
679
+ - **Default:** `true`
321
680
 
322
681
  ```tsx
323
682
  import React, { useState } from 'react';
324
683
  import { ChatInput } from '@app-studio/web';
325
684
 
326
- export const AudioChatInput = () => {
685
+ export const AutoFocusChatInput = () => {
327
686
  const [files, setFiles] = useState<File[]>([]);
328
687
 
329
688
  return (
330
689
  <ChatInput
331
- enableAudioRecording
332
- onAudioRecordingStart={() => console.log('Recording started')}
333
- onAudioRecordingStop={(audio) => console.log('Audio blob:', audio)}
690
+ autoFocus={false}
334
691
  onSubmit={(message) => console.log(message)}
335
692
  getPendingFiles={() => files}
336
693
  clearPendingFiles={() => setFiles([])}
@@ -339,6 +696,130 @@ export const AudioChatInput = () => {
339
696
  };
340
697
  ```
341
698
 
699
+ ### **errorMessage**
700
+ Error message to display at the bottom of the input.
701
+
702
+ - **Type:** `string`
703
+ - **Required:** `false`
704
+
705
+ ```tsx
706
+ import React, { useState } from 'react';
707
+ import { ChatInput } from '@app-studio/web';
708
+
709
+ export const ErrorMessageChatInput = () => {
710
+ const [files, setFiles] = useState<File[]>([]);
711
+ const [error, setError] = useState<string>('');
712
+
713
+ return (
714
+ <ChatInput
715
+ errorMessage={error}
716
+ onSubmit={(message) => {
717
+ if (!message.trim()) {
718
+ setError('Message cannot be empty');
719
+ return;
720
+ }
721
+ setError('');
722
+ console.log(message);
723
+ }}
724
+ getPendingFiles={() => files}
725
+ clearPendingFiles={() => setFiles([])}
726
+ />
727
+ );
728
+ };
729
+ ```
730
+
731
+ ### **shape**
732
+ Shape of the input container.
733
+
734
+ - **Type:** `Shape`
735
+ - **Default:** `'rounded'`
736
+ - **Possible Values:** `'rounded' | 'square'`
737
+
738
+ ```tsx
739
+ import React, { useState } from 'react';
740
+ import { ChatInput } from '@app-studio/web';
741
+ import { Vertical } from 'app-studio';
742
+
743
+ export const ShapeChatInputs = () => {
744
+ const [files, setFiles] = useState<File[]>([]);
745
+
746
+ return (
747
+ <Vertical gap={15}>
748
+ <ChatInput
749
+ shape="rounded"
750
+ onSubmit={(message) => console.log(message)}
751
+ getPendingFiles={() => files}
752
+ clearPendingFiles={() => setFiles([])}
753
+ />
754
+ <ChatInput
755
+ shape="square"
756
+ onSubmit={(message) => console.log(message)}
757
+ getPendingFiles={() => files}
758
+ clearPendingFiles={() => setFiles([])}
759
+ />
760
+ </Vertical>
761
+ );
762
+ };
763
+ ```
764
+
765
+ ### **enableAudioRecording**
766
+ Enable audio recording button. When enabled, users can record audio which is automatically converted to a file and uploaded using the same file upload workflow.
767
+
768
+ - **Type:** `boolean`
769
+ - **Default:** `false`
770
+
771
+ ### **onAudioRecordingStart**
772
+ Callback when audio recording starts.
773
+
774
+ - **Type:** `() => void`
775
+ - **Required:** `false`
776
+
777
+ ### **onAudioRecordingStop**
778
+ Callback when audio recording stops. The recorded audio is provided as a Blob and is automatically added to the upload queue.
779
+
780
+ - **Type:** `(audio: Blob) => void`
781
+ - **Required:** `false`
782
+
783
+ ```tsx
784
+ import React, { useState } from 'react';
785
+ import { ChatInput } from '@app-studio/web';
786
+
787
+ export const AudioChatInput = () => {
788
+ const [files, setFiles] = useState<File[]>([]);
789
+ const [isRecording, setIsRecording] = useState(false);
790
+
791
+ return (
792
+ <>
793
+ {isRecording && <div>🎤 Recording...</div>}
794
+
795
+ <ChatInput
796
+ enableAudioRecording
797
+ onAudioRecordingStart={() => {
798
+ console.log('Recording started');
799
+ setIsRecording(true);
800
+ }}
801
+ onAudioRecordingStop={(audioBlob) => {
802
+ console.log('Recording stopped, audio blob:', audioBlob);
803
+ setIsRecording(false);
804
+ // The audio file is automatically added to the upload queue
805
+ }}
806
+ onFileUpload={(file) => {
807
+ console.log('Uploading file (could be audio):', file.name);
808
+ // Handle upload for both regular files and audio recordings
809
+ }}
810
+ onSubmit={(message) => {
811
+ console.log('Message:', message);
812
+ console.log('Files (including audio):', files);
813
+ }}
814
+ getPendingFiles={() => files}
815
+ clearPendingFiles={() => setFiles([])}
816
+ />
817
+ </>
818
+ );
819
+ };
820
+ ```
821
+
822
+
342
823
  ### **suggestions**
343
824
  List of suggestions for auto-completion.
344
825
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@app-studio/web",
3
- "version": "0.9.65",
3
+ "version": "0.9.67",
4
4
  "main": "dist/index.js",
5
5
  "typings": "dist/components/index.d.ts",
6
6
  "files": [