@vertesia/ui 1.0.0-dev.20260227.112605Z → 1.0.0-dev.20260305.083323Z

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/lib/esm/core/components/shadcn/selectBox.js +1 -1
  2. package/lib/esm/core/components/shadcn/theme/ThemeSwitcher.js +3 -4
  3. package/lib/esm/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
  4. package/lib/esm/core/components/shadcn/tooltip.js +1 -1
  5. package/lib/esm/core/components/shadcn/tooltip.js.map +1 -1
  6. package/lib/esm/features/agent/chat/ModernAgentConversation.js +3 -3
  7. package/lib/esm/features/agent/chat/ModernAgentConversation.js.map +1 -1
  8. package/lib/esm/features/store/collections/CreateCollection.js +13 -13
  9. package/lib/esm/features/store/collections/CreateCollection.js.map +1 -1
  10. package/lib/esm/features/store/types/SelectContentType.js +1 -1
  11. package/lib/esm/features/store/types/SelectContentType.js.map +1 -1
  12. package/lib/esm/session/UserSession.js +8 -0
  13. package/lib/esm/session/UserSession.js.map +1 -1
  14. package/lib/esm/shell/login/TerminalLogin.js +1 -1
  15. package/lib/esm/shell/login/TerminalLogin.js.map +1 -1
  16. package/lib/tsconfig.tsbuildinfo +1 -1
  17. package/lib/types/core/components/shadcn/theme/ThemeSwitcher.d.ts +6 -1
  18. package/lib/types/core/components/shadcn/theme/ThemeSwitcher.d.ts.map +1 -1
  19. package/lib/types/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
  20. package/lib/types/features/store/collections/CreateCollection.d.ts.map +1 -1
  21. package/lib/types/session/UserSession.d.ts.map +1 -1
  22. package/lib/types/shell/login/TerminalLogin.d.ts.map +1 -1
  23. package/lib/vertesia-ui-core.js +1 -1
  24. package/lib/vertesia-ui-core.js.map +1 -1
  25. package/lib/vertesia-ui-features.js +1 -1
  26. package/lib/vertesia-ui-features.js.map +1 -1
  27. package/lib/vertesia-ui-session.js +1 -1
  28. package/lib/vertesia-ui-session.js.map +1 -1
  29. package/lib/vertesia-ui-shell.js.map +1 -1
  30. package/llms.txt +124 -4
  31. package/package.json +19 -5
  32. package/src/core/components/shadcn/selectBox.tsx +1 -1
  33. package/src/core/components/shadcn/theme/ThemeSwitcher.tsx +13 -8
  34. package/src/core/components/shadcn/tooltip.tsx +1 -1
  35. package/src/css/base.css +31 -0
  36. package/src/css/color.css +136 -0
  37. package/src/css/custom-tooltips.css +64 -0
  38. package/src/css/index.css +27 -0
  39. package/src/css/theme.css +86 -0
  40. package/src/css/typography.css +88 -0
  41. package/src/css/utilities.css +72 -0
  42. package/src/features/agent/chat/ModernAgentConversation.tsx +84 -91
  43. package/src/features/store/collections/CreateCollection.tsx +36 -34
  44. package/src/features/store/types/SelectContentType.tsx +1 -1
  45. package/src/session/UserSession.ts +8 -0
  46. package/src/shell/login/TerminalLogin.tsx +1 -2
@@ -497,91 +497,85 @@ function StartWorkflowView({
497
497
  }
498
498
 
499
499
  return (
500
- <div
501
- className={`flex flex-col h-full bg-white dark:bg-gray-900 overflow-hidden border-0 relative ${isDragOver ? 'ring-2 ring-blue-400 ring-inset' : ''}`}
502
- onDragEnter={handleDragEnter}
503
- onDragOver={handleDragOver}
504
- onDragLeave={handleDragLeave}
505
- onDrop={handleDrop}
506
- >
507
- {/* Drag overlay for full-panel file drop */}
508
- {isDragOver && (
509
- <div className="absolute inset-0 flex items-center justify-center bg-blue-100/80 dark:bg-blue-900/40 z-50 pointer-events-none rounded-lg">
510
- <div className="text-blue-600 dark:text-blue-400 font-medium flex items-center gap-2 text-lg">
511
- <UploadIcon className="size-6" />
512
- Drop files to stage for upload
513
- </div>
514
- </div>
515
- )}
516
-
517
- {/* Hidden file input */}
518
- <input
519
- ref={fileInputRef}
520
- type="file"
521
- multiple
522
- accept={acceptedFileTypes}
523
- onChange={handleFileInputChange}
524
- className="hidden"
525
- />
526
-
527
- {/* Header */}
528
- <div className="flex items-center justify-between py-2 px-3 border-b border-gray-100 dark:border-gray-800 bg-white dark:bg-gray-900">
529
- <div className="flex items-center space-x-2">
530
- <div className="p-1">
531
- <Cpu className="size-3.5 text-muted" />
532
- </div>
533
- <span className="font-medium text-sm text-gray-700 dark:text-gray-300">
534
- {title}
535
- </span>
536
- </div>
537
-
538
- {/* Close button if needed */}
539
- {onClose && !isModal && (
540
- <Button
541
- size="xs"
542
- variant="ghost"
543
- onClick={onClose}
544
- title="Close"
545
- className="text-slate-500 hover:text-slate-700"
546
- >
547
- <XIcon className="size-4" />
548
- </Button>
549
- )}
550
- </div>
551
-
552
- {/* Empty conversation area with instructions */}
500
+ <div className="flex flex-col h-full bg-background items-center">
553
501
  <div
554
- className={`flex-1 overflow-y-auto px-4 py-6 bg-white dark:bg-gray-900 flex flex-col ${
555
- fullWidth ? 'items-start justify-start' : 'items-center justify-center'
556
- }`}
502
+ className={cn(
503
+ "flex flex-col h-full w-full overflow-hidden border-0 relative",
504
+ fullWidth ? "" : "max-w-4xl"
505
+ )}
506
+ onDragEnter={handleDragEnter}
507
+ onDragOver={handleDragOver}
508
+ onDragLeave={handleDragLeave}
509
+ onDrop={handleDrop}
557
510
  >
558
- {initialMessage && (
559
- <div
560
- className={`px-4 py-3 mb-4 bg-blue-50/80 dark:bg-blue-900/30 border-l-2 border-blue-400 dark:border-blue-500 text-blue-700 dark:text-blue-300 ${
561
- fullWidth ? 'w-full' : 'max-w-md'
562
- }`}
563
- >
564
- {initialMessage}
511
+ {/* Drag overlay for full-panel file drop */}
512
+ {isDragOver && (
513
+ <div className="absolute inset-0 flex items-center justify-center bg-info-background z-50 pointer-events-none rounded-lg">
514
+ <div className="text-info font-medium flex items-center gap-2 text-lg">
515
+ <UploadIcon className="size-6" />
516
+ Drop files to stage for upload
517
+ </div>
565
518
  </div>
566
519
  )}
567
520
 
568
- <div
569
- className={`bg-white dark:bg-slate-800 p-4 border-l-2 border-blue-400 dark:border-blue-500 ${
570
- fullWidth ? 'w-full' : 'max-w-md'
571
- }`}
572
- >
573
- <div className="text-base text-slate-600 dark:text-slate-300 font-medium">
574
- Enter a message to start a conversation
521
+ {/* Hidden file input */}
522
+ <input
523
+ ref={fileInputRef}
524
+ type="file"
525
+ multiple
526
+ accept={acceptedFileTypes}
527
+ onChange={handleFileInputChange}
528
+ className="hidden"
529
+ />
530
+
531
+ {/* Header */}
532
+ <div className="flex items-center justify-between py-2 px-3 border-b border-border bg-background">
533
+ <div className="flex items-center space-x-2">
534
+ <div className="p-1">
535
+ <Cpu className="size-3.5 text-muted" />
536
+ </div>
537
+ <span className="font-medium text-sm text-foreground">
538
+ {title}
539
+ </span>
575
540
  </div>
576
- <div className="mt-3 text-sm text-slate-500 dark:text-slate-400">
577
- Type your question below and press Enter or click {startButtonText}{" "}
578
- to begin
541
+
542
+ {/* Close button if needed */}
543
+ {onClose && !isModal && (
544
+ <Button
545
+ size="xs"
546
+ variant="ghost"
547
+ onClick={onClose}
548
+ title="Close"
549
+ className="text-muted hover:text-foreground"
550
+ >
551
+ <XIcon className="size-4" />
552
+ </Button>
553
+ )}
554
+ </div>
555
+
556
+ {/* Empty conversation area with instructions */}
557
+ <div className="flex-1 overflow-y-auto bg-background flex flex-col items-center justify-end">
558
+ <div className="w-full px-4 py-6">
559
+ {initialMessage && (
560
+ <div className="px-4 py-3 mb-4 bg-info-background border-l-2 border-info text-info">
561
+ {initialMessage}
562
+ </div>
563
+ )}
564
+
565
+ <div className="bg-card p-4 border-l-2 border-info">
566
+ <div className="text-base text-foreground font-medium">
567
+ Enter a message to start a conversation
568
+ </div>
569
+ <div className="mt-3 text-sm text-muted">
570
+ Type your question below and press Enter or click {startButtonText}{" "}
571
+ to begin
572
+ </div>
573
+ </div>
579
574
  </div>
580
575
  </div>
581
- </div>
582
576
 
583
- {/* Input Area */}
584
- <div className="py-3 px-3 border-t border-gray-100 dark:border-gray-800 bg-white dark:bg-gray-900">
577
+ {/* Input Area */}
578
+ <div className="py-3 px-3 border-t border-border bg-background">
585
579
  {/* Staged files display */}
586
580
  {stagedFiles.length > 0 && (
587
581
  <div className="flex flex-wrap gap-2 mb-3">
@@ -619,23 +613,21 @@ function StartWorkflowView({
619
613
  </div>
620
614
 
621
615
  <div className="flex items-end gap-2">
622
- <div className="flex-1">
623
- <textarea
624
- ref={inputRef}
625
- value={inputValue}
626
- onChange={(e) => setInputValue(e.target.value)}
627
- onKeyDown={handleKeyDown}
628
- placeholder={placeholder}
629
- disabled={isSending}
630
- rows={2}
631
- className="w-full py-2.5 px-3 text-sm border border-gray-200 dark:border-gray-700 dark:bg-gray-800 dark:text-white focus:border-gray-300 dark:focus:border-gray-600 focus:outline-none focus:ring-1 focus:ring-gray-300 dark:focus:ring-gray-600 rounded-md resize-none overflow-hidden"
632
- style={{ minHeight: '60px', maxHeight: '200px' }}
633
- />
634
- </div>
616
+ <textarea
617
+ ref={inputRef}
618
+ value={inputValue}
619
+ onChange={(e) => setInputValue(e.target.value)}
620
+ onKeyDown={handleKeyDown}
621
+ placeholder={placeholder}
622
+ disabled={isSending}
623
+ rows={2}
624
+ className="flex-1 py-2.5 px-3 text-sm border border-border bg-background text-foreground focus:border-ring focus:outline-none focus:ring-1 focus:ring-ring rounded-md resize-none overflow-hidden"
625
+ style={{ minHeight: '60px', maxHeight: '200px' }}
626
+ />
635
627
  <Button
636
628
  onClick={startWorkflowWithMessage}
637
629
  disabled={!inputValue.trim() || isSending}
638
- className="px-3 py-2.5 bg-gray-800 dark:bg-gray-700 hover:bg-gray-700 dark:hover:bg-gray-600 text-white text-xs rounded-md transition-colors"
630
+ className="px-3 py-2.5 text-xs rounded-md transition-colors"
639
631
  >
640
632
  {isSending ? (
641
633
  <Spinner size="sm" className="mr-1.5" />
@@ -645,13 +637,14 @@ function StartWorkflowView({
645
637
  {startButtonText}
646
638
  </Button>
647
639
  </div>
648
- <div className="text-xs text-gray-400 dark:text-gray-500 mt-2 text-center">
640
+ <div className="text-xs text-muted mt-2 text-center">
649
641
  {stagedFiles.length > 0
650
642
  ? `${stagedFiles.length} file${stagedFiles.length > 1 ? 's' : ''} staged - will upload when conversation starts`
651
643
  : 'Enter to send • Shift+Enter for new line'}
652
644
  </div>
653
645
  </div>
654
646
  </div>
647
+ </div>
655
648
  );
656
649
  }
657
650
 
@@ -74,55 +74,57 @@ export function CreateCollectionForm({ onClose, redirect = true, onAddToCollecti
74
74
  };
75
75
 
76
76
  return (
77
- <form onSubmit={(e) => e.preventDefault()}>
77
+ <>
78
78
  <ModalBody>
79
- <FormItem label="Name" required>
80
- <Input type="text" value={payload.name || ""} onChange={(value) => setPayloadProp("name", value)} />
81
- </FormItem>
82
- <FormItem label="Description" className="mt-2">
83
- <Textarea
84
- value={payload.description || ""}
85
- onChange={(ev) => setPayloadProp("description", ev.target.value)}
86
- />
87
- </FormItem>
88
- <FormItem label="Dynamic Collection" className="mt-2" direction="row" description="Dynamically fetch content for the collection based on a query. If not enabled, then content must be added by users or agents.">
89
- <Switch value={payload.dynamic || false} onChange={(value) => setPayloadProp("dynamic", value)} />
90
- </FormItem>
91
- { !payload.dynamic &&
92
- <FormItem label="Allowed Content Types" className="mt-4" description="Optionally select which content types can be added to the collection. If not set, then all content types are allowed.">
79
+ <form onSubmit={(e) => e.preventDefault()}>
80
+ <FormItem label="Name" required>
81
+ <Input type="text" value={payload.name || ""} onChange={(value) => setPayloadProp("name", value)} />
82
+ </FormItem>
83
+ <FormItem label="Description" className="mt-2">
84
+ <Textarea
85
+ value={payload.description || ""}
86
+ onChange={(ev) => setPayloadProp("description", ev.target.value)}
87
+ />
88
+ </FormItem>
89
+ <FormItem label="Dynamic Collection" className="mt-2" direction="row" description="Dynamically fetch content for the collection based on a query. If not enabled, then content must be added by users or agents.">
90
+ <Switch value={payload.dynamic || false} onChange={(value) => setPayloadProp("dynamic", value)} />
91
+ </FormItem>
92
+ {!payload.dynamic &&
93
+ <FormItem label="Allowed Content Types" className="mt-4" description="Optionally select which content types can be added to the collection. If not set, then all content types are allowed.">
94
+ <SelectContentType
95
+ defaultValue={payload.allowed_types || null}
96
+ onChange={(v) => {
97
+ if (Array.isArray(v)) {
98
+ setPayloadProp("allowed_types", v.map(type => type.id));
99
+ } else {
100
+ setPayloadProp("allowed_types", v ? [v.id] : []);
101
+ }
102
+ }}
103
+ isClearable multiple
104
+ />
105
+ </FormItem>
106
+ }
107
+ <FormItem label="Type" className="mt-2" description="Optionally select a content type to assign custom properties and data to the collection.">
93
108
  <SelectContentType
94
- defaultValue={payload.allowed_types || null}
109
+ defaultValue={payload.type || null}
95
110
  onChange={(v) => {
96
111
  if (Array.isArray(v)) {
97
- setPayloadProp("allowed_types", v.map(type => type.id));
112
+ setPayloadProp("type", v.length > 0 ? v[0].id : null);
98
113
  } else {
99
- setPayloadProp("allowed_types", v ? [v.id] : []);
114
+ setPayloadProp("type", v?.id || null);
100
115
  }
101
116
  }}
102
- isClearable multiple
117
+ isClearable
103
118
  />
104
119
  </FormItem>
105
- }
106
- <FormItem label="Type" className="mt-2" description="Optionally select a content type to assign custom properties and data to the collection.">
107
- <SelectContentType
108
- defaultValue={payload.type || null}
109
- onChange={(v) => {
110
- if (Array.isArray(v)) {
111
- setPayloadProp("type", v.length > 0 ? v[0].id : null);
112
- } else {
113
- setPayloadProp("type", v?.id || null);
114
- }
115
- }}
116
- isClearable
117
- />
118
- </FormItem>
120
+ </form>
119
121
  </ModalBody>
120
122
  <ModalFooter>
121
123
  <Button isDisabled={isProcessing} onClick={onCreate}>
122
124
  Create Collection
123
125
  </Button>
124
126
  </ModalFooter>
125
- </form >
127
+ </>
126
128
  );
127
129
  }
128
130
 
@@ -9,7 +9,7 @@ const optionLabel = (t: ContentObjectTypeItem | null) => {
9
9
  return (
10
10
  <div>
11
11
  <div className="text-sm">{t.name}</div>
12
- <div className="text-xs text-gray-500">{t.description}</div>
12
+ <div className="text-xs text-muted truncate">{t.description}</div>
13
13
  </div>
14
14
  );
15
15
  };
@@ -137,6 +137,7 @@ class UserSession {
137
137
  } else {
138
138
  // Use Firebase logout directly
139
139
  console.log('Using Firebase logout');
140
+ const wasLoggedIn = !!this.authToken;
140
141
  if (this.authToken) {
141
142
  getFirebaseAuth().signOut();
142
143
  }
@@ -145,6 +146,13 @@ class UserSession {
145
146
  this.authToken = undefined;
146
147
  this.setSession = undefined;
147
148
  this.client.withAuthCallback(undefined);
149
+ // Navigate to root to avoid React rendering errors when
150
+ // unmounting deeply nested route components during logout.
151
+ // Only navigate if user was actually logged in to avoid
152
+ // infinite reload loop on fresh/incognito sessions.
153
+ if (wasLoggedIn) {
154
+ location.replace("/");
155
+ }
148
156
  }
149
157
  }
150
158
 
@@ -1,10 +1,9 @@
1
- import { useState } from 'react'
2
-
3
1
  import { AccountRef, ProjectRef } from '@vertesia/common'
4
2
  import { Button, Center, ErrorBox, Input, SelectBox, Spinner, useFetch, useToast } from '@vertesia/ui/core'
5
3
  import { Env } from "@vertesia/ui/env"
6
4
  import { useLocation } from "@vertesia/ui/router"
7
5
  import { fetchComposableTokenFromFirebaseToken, useUserSession } from '@vertesia/ui/session'
6
+ import { useState } from 'react'
8
7
 
9
8
  interface ProfileData {
10
9
  profile?: string