@vertesia/ui 1.0.0-dev.20260225.024852Z → 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.
- package/lib/esm/core/components/shadcn/selectBox.js +1 -1
- package/lib/esm/core/components/shadcn/theme/ThemeSwitcher.js +3 -4
- package/lib/esm/core/components/shadcn/theme/ThemeSwitcher.js.map +1 -1
- package/lib/esm/core/components/shadcn/tooltip.js +1 -1
- package/lib/esm/core/components/shadcn/tooltip.js.map +1 -1
- package/lib/esm/env/index.js +3 -0
- package/lib/esm/env/index.js.map +1 -1
- package/lib/esm/features/agent/chat/ModernAgentConversation.js +3 -3
- package/lib/esm/features/agent/chat/ModernAgentConversation.js.map +1 -1
- package/lib/esm/features/agent/chat/ModernAgentOutput/utils.js +8 -1
- package/lib/esm/features/agent/chat/ModernAgentOutput/utils.js.map +1 -1
- package/lib/esm/features/facets/CollectionsFacetsNav.js +2 -2
- package/lib/esm/features/facets/CollectionsFacetsNav.js.map +1 -1
- package/lib/esm/features/facets/DocumentsFacetsNav.js +2 -2
- package/lib/esm/features/facets/DocumentsFacetsNav.js.map +1 -1
- package/lib/esm/features/facets/utils/TypeFacet.js +2 -2
- package/lib/esm/features/facets/utils/TypeFacet.js.map +1 -1
- package/lib/esm/features/store/collections/BrowseCollectionView.js +3 -1
- package/lib/esm/features/store/collections/BrowseCollectionView.js.map +1 -1
- package/lib/esm/features/store/collections/CreateCollection.js +13 -13
- package/lib/esm/features/store/collections/CreateCollection.js.map +1 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js +2 -1
- package/lib/esm/features/store/objects/DocumentSearchResults.js.map +1 -1
- package/lib/esm/features/store/objects/upload/DocumentUploadModal.js +3 -1
- package/lib/esm/features/store/objects/upload/DocumentUploadModal.js.map +1 -1
- package/lib/esm/features/store/types/ContentObjectTypesSearch.js +4 -3
- package/lib/esm/features/store/types/ContentObjectTypesSearch.js.map +1 -1
- package/lib/esm/features/store/types/SelectContentType.js +9 -9
- package/lib/esm/features/store/types/SelectContentType.js.map +1 -1
- package/lib/esm/features/store/types/SelectContentTypeModal.js +2 -2
- package/lib/esm/features/store/types/SelectContentTypeModal.js.map +1 -1
- package/lib/esm/features/store/types/TypeRegistry.js.map +1 -0
- package/lib/esm/features/store/types/TypeRegistryProvider.js +53 -0
- package/lib/esm/features/store/types/TypeRegistryProvider.js.map +1 -0
- package/lib/esm/features/store/types/index.js +2 -0
- package/lib/esm/features/store/types/index.js.map +1 -1
- package/lib/esm/session/UserSession.js +9 -27
- package/lib/esm/session/UserSession.js.map +1 -1
- package/lib/esm/session/index.js +0 -1
- package/lib/esm/session/index.js.map +1 -1
- package/lib/esm/shell/SplashScreen.js +2 -2
- package/lib/esm/shell/SplashScreen.js.map +1 -1
- package/lib/esm/shell/VertesiaShell.js +2 -2
- package/lib/esm/shell/VertesiaShell.js.map +1 -1
- package/lib/esm/shell/login/TerminalLogin.js +1 -1
- package/lib/esm/shell/login/TerminalLogin.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/core/components/shadcn/theme/ThemeSwitcher.d.ts +6 -1
- package/lib/types/core/components/shadcn/theme/ThemeSwitcher.d.ts.map +1 -1
- package/lib/types/env/index.d.ts +2 -0
- package/lib/types/env/index.d.ts.map +1 -1
- package/lib/types/features/agent/chat/ModernAgentConversation.d.ts.map +1 -1
- package/lib/types/features/agent/chat/ModernAgentOutput/utils.d.ts.map +1 -1
- package/lib/types/features/facets/CollectionsFacetsNav.d.ts.map +1 -1
- package/lib/types/features/facets/utils/TypeFacet.d.ts.map +1 -1
- package/lib/types/features/facets/utils/VTypeFacet.d.ts +1 -1
- package/lib/types/features/facets/utils/VTypeFacet.d.ts.map +1 -1
- package/lib/types/features/store/collections/BrowseCollectionView.d.ts.map +1 -1
- package/lib/types/features/store/collections/CreateCollection.d.ts.map +1 -1
- package/lib/types/features/store/objects/DocumentSearchResults.d.ts.map +1 -1
- package/lib/types/features/store/objects/upload/DocumentUploadModal.d.ts.map +1 -1
- package/lib/types/features/store/types/ContentObjectTypesSearch.d.ts.map +1 -1
- package/lib/types/features/store/types/TypeRegistry.d.ts.map +1 -0
- package/lib/types/features/store/types/TypeRegistryProvider.d.ts +15 -0
- package/lib/types/features/store/types/TypeRegistryProvider.d.ts.map +1 -0
- package/lib/types/features/store/types/index.d.ts +2 -0
- package/lib/types/features/store/types/index.d.ts.map +1 -1
- package/lib/types/session/UserSession.d.ts +0 -4
- package/lib/types/session/UserSession.d.ts.map +1 -1
- package/lib/types/session/index.d.ts +0 -1
- package/lib/types/session/index.d.ts.map +1 -1
- package/lib/types/shell/VertesiaShell.d.ts.map +1 -1
- package/lib/types/shell/login/TerminalLogin.d.ts.map +1 -1
- package/lib/vertesia-ui-core.js +1 -1
- package/lib/vertesia-ui-core.js.map +1 -1
- package/lib/vertesia-ui-env.js +1 -1
- package/lib/vertesia-ui-env.js.map +1 -1
- package/lib/vertesia-ui-features.js +1 -1
- package/lib/vertesia-ui-features.js.map +1 -1
- package/lib/vertesia-ui-session.js +1 -1
- package/lib/vertesia-ui-session.js.map +1 -1
- package/lib/vertesia-ui-shell.js +1 -1
- package/lib/vertesia-ui-shell.js.map +1 -1
- package/llms.txt +124 -4
- package/package.json +29 -7
- package/src/core/components/shadcn/selectBox.tsx +1 -1
- package/src/core/components/shadcn/theme/ThemeSwitcher.tsx +13 -8
- package/src/core/components/shadcn/tooltip.tsx +1 -1
- package/src/css/base.css +31 -0
- package/src/css/color.css +136 -0
- package/src/css/custom-tooltips.css +64 -0
- package/src/css/index.css +27 -0
- package/src/css/theme.css +86 -0
- package/src/css/typography.css +88 -0
- package/src/css/utilities.css +72 -0
- package/src/env/index.ts +5 -0
- package/src/features/agent/chat/ModernAgentConversation.tsx +84 -91
- package/src/features/agent/chat/ModernAgentOutput/utils.ts +7 -1
- package/src/features/facets/CollectionsFacetsNav.tsx +2 -2
- package/src/features/facets/DocumentsFacetsNav.tsx +2 -2
- package/src/features/facets/utils/TypeFacet.tsx +2 -2
- package/src/features/facets/utils/VTypeFacet.tsx +1 -1
- package/src/features/store/collections/BrowseCollectionView.tsx +5 -2
- package/src/features/store/collections/CreateCollection.tsx +36 -34
- package/src/features/store/objects/DocumentSearchResults.tsx +4 -2
- package/src/features/store/objects/upload/DocumentUploadModal.tsx +3 -1
- package/src/features/store/types/ContentObjectTypesSearch.tsx +4 -3
- package/src/features/store/types/SelectContentType.tsx +9 -9
- package/src/features/store/types/SelectContentTypeModal.tsx +2 -2
- package/src/features/store/types/TypeRegistryProvider.tsx +73 -0
- package/src/features/store/types/index.ts +2 -0
- package/src/session/UserSession.ts +9 -28
- package/src/session/index.ts +0 -1
- package/src/shell/SplashScreen.tsx +4 -2
- package/src/shell/VertesiaShell.tsx +10 -8
- package/src/shell/login/TerminalLogin.tsx +1 -2
- package/lib/esm/session/TypeRegistry.js.map +0 -1
- package/lib/types/session/TypeRegistry.d.ts.map +0 -1
- /package/lib/esm/{session → features/store/types}/TypeRegistry.js +0 -0
- /package/lib/types/{session → features/store/types}/TypeRegistry.d.ts +0 -0
- /package/src/{session → features/store/types}/TypeRegistry.ts +0 -0
|
@@ -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={
|
|
555
|
-
|
|
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
|
-
{
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
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
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
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
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
584
|
-
|
|
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
|
-
<
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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
|
|
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-
|
|
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
|
|
|
@@ -363,7 +363,13 @@ export function groupMessagesWithStreaming(
|
|
|
363
363
|
for (const message of messages) {
|
|
364
364
|
const activityGroupId = getActivityGroupId(message);
|
|
365
365
|
|
|
366
|
-
|
|
366
|
+
// Certain message types have dedicated renderers (AskUserWidget for
|
|
367
|
+
// REQUEST_INPUT, BatchProgressPanel for BATCH_PROGRESS) and must stay
|
|
368
|
+
// standalone so MessageItem / AllMessagesMixed can route them correctly.
|
|
369
|
+
if (message.type === AgentMessageType.REQUEST_INPUT
|
|
370
|
+
|| message.type === AgentMessageType.BATCH_PROGRESS) {
|
|
371
|
+
standaloneMessages.push(message);
|
|
372
|
+
} else if (activityGroupId) {
|
|
367
373
|
if (!activityGroups.has(activityGroupId)) {
|
|
368
374
|
activityGroups.set(activityGroupId, {
|
|
369
375
|
messages: [],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Filter as BaseFilter, FilterProvider, FilterBtn, FilterBar, FilterClear, FilterGroup } from '@vertesia/ui/core';
|
|
2
2
|
import { useState } from 'react';
|
|
3
|
+
import { useTypeRegistry } from '../store/types/TypeRegistryProvider.js';
|
|
3
4
|
import { SearchInterface } from './utils/SearchInterface';
|
|
4
|
-
import { useUserSession } from '@vertesia/ui/session';
|
|
5
5
|
|
|
6
6
|
interface CollectionsFacetsNavProps {
|
|
7
7
|
facets: {
|
|
@@ -14,7 +14,7 @@ interface CollectionsFacetsNavProps {
|
|
|
14
14
|
// Hook to create filter groups for collections
|
|
15
15
|
export function useCollectionsFilterGroups(facets: CollectionsFacetsNavProps['facets']): FilterGroup[] {
|
|
16
16
|
void facets;
|
|
17
|
-
const { typeRegistry } =
|
|
17
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
18
18
|
|
|
19
19
|
const customFilterGroups: FilterGroup[] = [];
|
|
20
20
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Filter as BaseFilter, FilterProvider, FilterBtn, FilterBar, FilterClear, FilterGroup } from '@vertesia/ui/core';
|
|
2
|
-
import { useUserSession } from '@vertesia/ui/session';
|
|
3
2
|
import { useState } from 'react';
|
|
3
|
+
import { useTypeRegistry } from '../store/types/TypeRegistryProvider.js';
|
|
4
4
|
import { VStringFacet } from './utils/VStringFacet';
|
|
5
5
|
import { VTypeFacet } from './utils/VTypeFacet';
|
|
6
6
|
import { SearchInterface } from './utils/SearchInterface';
|
|
@@ -18,7 +18,7 @@ interface DocumentsFacetsNavProps {
|
|
|
18
18
|
|
|
19
19
|
// Hook to create filter groups for documents
|
|
20
20
|
export function useDocumentFilterGroups(facets: DocumentsFacetsNavProps['facets']): FilterGroup[] {
|
|
21
|
-
const { typeRegistry } =
|
|
21
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
22
22
|
const customFilterGroups: FilterGroup[] = [];
|
|
23
23
|
|
|
24
24
|
customFilterGroups.push({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useUserSession } from "@vertesia/ui/session";
|
|
2
1
|
import { FacetBucket } from "@vertesia/common";
|
|
3
2
|
import { SelectBox } from "@vertesia/ui/core";
|
|
4
3
|
import { useEffect, useState } from "react";
|
|
4
|
+
import { useTypeRegistry } from "../../store/types/TypeRegistryProvider.js";
|
|
5
5
|
|
|
6
6
|
interface TypeFacetBucket {
|
|
7
7
|
name: string;
|
|
@@ -21,7 +21,7 @@ interface TypeFacetProps {
|
|
|
21
21
|
}
|
|
22
22
|
export function TypeFacet({ search, buckets, placeholder = "Filter by Type", className }: TypeFacetProps) {
|
|
23
23
|
const [options, setOptions] = useState<TypeFacetBucket[]>([]);
|
|
24
|
-
const { typeRegistry } =
|
|
24
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
25
25
|
const filterValue = search.getFilterValue("type") as string;
|
|
26
26
|
const onChange = (option: FacetBucket | undefined) => {
|
|
27
27
|
search.setFilterValue("type", option?._id);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FacetBucket } from "@vertesia/common";
|
|
2
2
|
import { FilterGroup } from "@vertesia/ui/core";
|
|
3
|
-
import { TypeRegistry } from "
|
|
3
|
+
import { TypeRegistry } from "../../store/types/TypeRegistry.js";
|
|
4
4
|
|
|
5
5
|
interface VTypeFacetProps {
|
|
6
6
|
buckets: FacetBucket[];
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { Collection, getContentTypeRefId } from "@vertesia/common";
|
|
2
2
|
import { useToast } from "@vertesia/ui/core";
|
|
3
|
-
import {
|
|
3
|
+
import { useUserSession } from "@vertesia/ui/session";
|
|
4
|
+
import { TypeRegistry } from "../types/TypeRegistry.js";
|
|
5
|
+
import { useTypeRegistry } from "../types/TypeRegistryProvider.js";
|
|
4
6
|
import { DocumentSearchResults, DocumentSearchResultsWithDropZone } from "../objects/DocumentSearchResults";
|
|
5
7
|
|
|
6
8
|
|
|
@@ -9,7 +11,8 @@ interface BrowseCollectionViewProps {
|
|
|
9
11
|
}
|
|
10
12
|
export function BrowseCollectionView({ collection }: BrowseCollectionViewProps) {
|
|
11
13
|
const toast = useToast();
|
|
12
|
-
const { client
|
|
14
|
+
const { client } = useUserSession();
|
|
15
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
13
16
|
|
|
14
17
|
const onUploadDone = async (objectIds: string[]) => {
|
|
15
18
|
if (objectIds.length > 0) {
|
|
@@ -74,55 +74,57 @@ export function CreateCollectionForm({ onClose, redirect = true, onAddToCollecti
|
|
|
74
74
|
};
|
|
75
75
|
|
|
76
76
|
return (
|
|
77
|
-
|
|
77
|
+
<>
|
|
78
78
|
<ModalBody>
|
|
79
|
-
<
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
<
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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.
|
|
109
|
+
defaultValue={payload.type || null}
|
|
95
110
|
onChange={(v) => {
|
|
96
111
|
if (Array.isArray(v)) {
|
|
97
|
-
setPayloadProp("
|
|
112
|
+
setPayloadProp("type", v.length > 0 ? v[0].id : null);
|
|
98
113
|
} else {
|
|
99
|
-
setPayloadProp("
|
|
114
|
+
setPayloadProp("type", v?.id || null);
|
|
100
115
|
}
|
|
101
116
|
}}
|
|
102
|
-
isClearable
|
|
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
|
-
|
|
127
|
+
</>
|
|
126
128
|
);
|
|
127
129
|
}
|
|
128
130
|
|
|
@@ -9,7 +9,9 @@ import {
|
|
|
9
9
|
SidePanel, Spinner, useIntersectionObserver, useToast
|
|
10
10
|
} from '@vertesia/ui/core';
|
|
11
11
|
import { useNavigate } from "@vertesia/ui/router";
|
|
12
|
-
import {
|
|
12
|
+
import { useUserSession } from '@vertesia/ui/session';
|
|
13
|
+
import { TypeRegistry } from '../types/TypeRegistry.js';
|
|
14
|
+
import { useTypeRegistry } from '../types/TypeRegistryProvider.js';
|
|
13
15
|
import { Download, ExternalLink, RefreshCw } from 'lucide-react';
|
|
14
16
|
import { useEffect, useRef, useState } from "react";
|
|
15
17
|
import { useDocumentFilterGroups, useDocumentFilterHandler } from "../../facets/DocumentsFacetsNav";
|
|
@@ -95,7 +97,7 @@ export function DocumentSearchResults({ layout, onUpload, allowFilter = true, al
|
|
|
95
97
|
const searchContext = useDocumentSearch();
|
|
96
98
|
const [isReady, setIsReady] = useState(false);
|
|
97
99
|
const [selectedObject, setSelectedObject] = useState<ContentObjectItem | null>(null);
|
|
98
|
-
const { typeRegistry } =
|
|
100
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
99
101
|
const { search, isLoading, error, objects, hasMore } = useWatchDocumentSearchResult();
|
|
100
102
|
const [actualLayout, setActualLayout] = useState<ColumnLayout[]>(
|
|
101
103
|
typeRegistry ? layout || getTableLayout(typeRegistry, search.query.type) : defaultLayout,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Collection, ContentObjectTypeItem, DynamicCollection } from "@vertesia/common";
|
|
2
2
|
import { Button, MessageBox, Modal, ModalBody, ModalFooter, ModalTitle, SelectBox, Spinner, useToast, VTooltip } from "@vertesia/ui/core";
|
|
3
3
|
import { useUserSession } from "@vertesia/ui/session";
|
|
4
|
+
import { useTypeRegistry } from "../../types/TypeRegistryProvider.js";
|
|
4
5
|
import { DropZone, UploadSummary } from '@vertesia/ui/widgets';
|
|
5
6
|
import { AlertCircleIcon, CheckCircleIcon, FileIcon, FolderIcon, Info, UploadIcon, XCircleIcon } from "lucide-react";
|
|
6
7
|
import { ReactNode, useEffect, useMemo, useState } from "react";
|
|
@@ -69,7 +70,8 @@ export function DocumentUploadModal({
|
|
|
69
70
|
showTypeSelectionOnly = false,
|
|
70
71
|
allowFolders = true,
|
|
71
72
|
}: DocumentUploadModalProps) {
|
|
72
|
-
const { client
|
|
73
|
+
const { client } = useUserSession();
|
|
74
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
73
75
|
const toast = useToast();
|
|
74
76
|
const [files, setFiles] = useState<File[]>([]);
|
|
75
77
|
const [processedFiles, setProcessedFiles] = useState<FileWithMetadata[]>([]);
|
|
@@ -5,6 +5,7 @@ import { useWatchSearchResult } from "./search/ObjectTypeSearchContext";
|
|
|
5
5
|
|
|
6
6
|
import { EmptyCollection, ErrorBox, Input, SelectBox, useDebounce, useIntersectionObserver, useToast } from "@vertesia/ui/core";
|
|
7
7
|
import { useUserSession } from "@vertesia/ui/session";
|
|
8
|
+
import { useTypeRegistry } from "./TypeRegistryProvider.js";
|
|
8
9
|
|
|
9
10
|
import { CreateOrUpdateTypeModal, CreateOrUpdateTypePayload } from "./CreateOrUpdateTypeModal";
|
|
10
11
|
|
|
@@ -14,8 +15,8 @@ interface ContentObjectTypesSearchProps {
|
|
|
14
15
|
isDirty?: boolean;
|
|
15
16
|
}
|
|
16
17
|
export function ContentObjectTypesSearch({ isDirty = false }: ContentObjectTypesSearchProps) {
|
|
17
|
-
const
|
|
18
|
-
const {
|
|
18
|
+
const { store } = useUserSession();
|
|
19
|
+
const { reload: reloadTypes } = useTypeRegistry();
|
|
19
20
|
|
|
20
21
|
const toast = useToast();
|
|
21
22
|
|
|
@@ -80,7 +81,7 @@ export function ContentObjectTypesSearch({ isDirty = false }: ContentObjectTypes
|
|
|
80
81
|
title: "Type created",
|
|
81
82
|
duration: 2000
|
|
82
83
|
});
|
|
83
|
-
|
|
84
|
+
reloadTypes();
|
|
84
85
|
search.search().then(() => setIsReady(true));
|
|
85
86
|
}).catch(err => {
|
|
86
87
|
toast({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ContentObjectTypeItem } from "@vertesia/common";
|
|
2
2
|
import { SelectBox } from "@vertesia/ui/core";
|
|
3
|
-
import { useUserSession } from "@vertesia/ui/session";
|
|
4
3
|
import { useEffect, useState } from "react";
|
|
4
|
+
import { useTypeRegistry } from "./TypeRegistryProvider.js";
|
|
5
5
|
|
|
6
6
|
const optionLabel = (t: ContentObjectTypeItem | null) => {
|
|
7
7
|
if (t === null) return 'None';
|
|
@@ -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-
|
|
12
|
+
<div className="text-xs text-muted truncate">{t.description}</div>
|
|
13
13
|
</div>
|
|
14
14
|
);
|
|
15
15
|
};
|
|
@@ -22,7 +22,7 @@ interface SelectContentTypeProps {
|
|
|
22
22
|
multiple?: boolean;
|
|
23
23
|
}
|
|
24
24
|
export function SelectContentType({ className, defaultValue, onChange, isClearable, multiple}: SelectContentTypeProps) {
|
|
25
|
-
const
|
|
25
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
26
26
|
const [isMounted, setIsMounted] = useState(false);
|
|
27
27
|
const [selectedType, setSelectedType] = useState<ContentObjectTypeItem | undefined>();
|
|
28
28
|
const [selectedTypes, setSelectedTypes] = useState<ContentObjectTypeItem[]>([])
|
|
@@ -30,18 +30,18 @@ export function SelectContentType({ className, defaultValue, onChange, isClearab
|
|
|
30
30
|
useEffect(() => {
|
|
31
31
|
if (!isMounted) {
|
|
32
32
|
setIsMounted(true);
|
|
33
|
-
if (
|
|
33
|
+
if (typeRegistry && defaultValue) {
|
|
34
34
|
if (multiple && Array.isArray(defaultValue)) {
|
|
35
|
-
const types =
|
|
35
|
+
const types = typeRegistry.types.filter(t => defaultValue.includes(t.id));
|
|
36
36
|
setSelectedTypes(types);
|
|
37
37
|
}
|
|
38
|
-
const type =
|
|
38
|
+
const type = typeRegistry.types.find(t => t.id === defaultValue);
|
|
39
39
|
if (type) {
|
|
40
40
|
setSelectedType(type);
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
|
-
}, [
|
|
44
|
+
}, [typeRegistry, defaultValue, multiple])
|
|
45
45
|
|
|
46
46
|
const _onChange = (option: ContentObjectTypeItem | null) => {
|
|
47
47
|
setSelectedType(option || undefined);
|
|
@@ -57,7 +57,7 @@ export function SelectContentType({ className, defaultValue, onChange, isClearab
|
|
|
57
57
|
return (
|
|
58
58
|
<div className='flex flex-col gap-4 content-between'>
|
|
59
59
|
<SelectBox<ContentObjectTypeItem>
|
|
60
|
-
options={
|
|
60
|
+
options={typeRegistry?.types || []}
|
|
61
61
|
value={selectedTypes}
|
|
62
62
|
onChange={_onChangeMultiple}
|
|
63
63
|
placeholder="Choose Content Types..."
|
|
@@ -74,7 +74,7 @@ export function SelectContentType({ className, defaultValue, onChange, isClearab
|
|
|
74
74
|
return (
|
|
75
75
|
<div className='flex flex-col gap-4 content-between'>
|
|
76
76
|
<SelectBox<ContentObjectTypeItem>
|
|
77
|
-
options={
|
|
77
|
+
options={typeRegistry?.types || []}
|
|
78
78
|
value={selectedType}
|
|
79
79
|
onChange={_onChange}
|
|
80
80
|
placeholder="Choose a Content Type..."
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
VTooltip,
|
|
10
10
|
} from "@vertesia/ui/core";
|
|
11
11
|
import { ContentObjectTypeItem } from "@vertesia/common";
|
|
12
|
-
import {
|
|
12
|
+
import { useTypeRegistry } from "./TypeRegistryProvider.js";
|
|
13
13
|
import { CheckCircleIcon, Info } from "lucide-react";
|
|
14
14
|
|
|
15
15
|
/**
|
|
@@ -49,7 +49,7 @@ export function SelectContentTypeModal({
|
|
|
49
49
|
initialSelectedType = null,
|
|
50
50
|
allowNone = true,
|
|
51
51
|
}: SelectContentTypeModalProps) {
|
|
52
|
-
const { typeRegistry } =
|
|
52
|
+
const { registry: typeRegistry } = useTypeRegistry();
|
|
53
53
|
const [selectedType, setSelectedType] = useState<ContentObjectTypeItem | null>(initialSelectedType);
|
|
54
54
|
|
|
55
55
|
// Get available types from the registry
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { createContext, ReactNode, useCallback, useContext, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useUserSession } from '@vertesia/ui/session';
|
|
4
|
+
import { TypeRegistry } from './TypeRegistry.js';
|
|
5
|
+
|
|
6
|
+
interface TypeRegistryContextValue {
|
|
7
|
+
registry: TypeRegistry | undefined;
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
load: () => Promise<void>;
|
|
10
|
+
reload: () => Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const TypeRegistryContext = createContext<TypeRegistryContextValue>({
|
|
14
|
+
registry: undefined,
|
|
15
|
+
isLoading: false,
|
|
16
|
+
load: () => Promise.resolve(),
|
|
17
|
+
reload: () => Promise.resolve(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
interface TypeRegistryProviderProps {
|
|
21
|
+
children: ReactNode;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function TypeRegistryProvider({ children }: TypeRegistryProviderProps) {
|
|
25
|
+
const { store, project } = useUserSession();
|
|
26
|
+
const [registry, setRegistry] = useState<TypeRegistry | undefined>();
|
|
27
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
28
|
+
const fetchRef = useRef(false);
|
|
29
|
+
|
|
30
|
+
const load = useCallback(async () => {
|
|
31
|
+
if (!project || fetchRef.current) return;
|
|
32
|
+
fetchRef.current = true;
|
|
33
|
+
setIsLoading(true);
|
|
34
|
+
try {
|
|
35
|
+
const types = await store.types.catalog.list({ layout: true });
|
|
36
|
+
setRegistry(new TypeRegistry(types));
|
|
37
|
+
} catch (err) {
|
|
38
|
+
console.error('Failed to fetch types', err);
|
|
39
|
+
} finally {
|
|
40
|
+
setIsLoading(false);
|
|
41
|
+
fetchRef.current = false;
|
|
42
|
+
}
|
|
43
|
+
}, [store, project]);
|
|
44
|
+
|
|
45
|
+
const reload = useCallback(async () => {
|
|
46
|
+
fetchRef.current = false;
|
|
47
|
+
return load();
|
|
48
|
+
}, [load]);
|
|
49
|
+
|
|
50
|
+
// Reset when project changes
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
setRegistry(undefined);
|
|
53
|
+
fetchRef.current = false;
|
|
54
|
+
}, [project]);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<TypeRegistryContext.Provider value={{ registry, isLoading, load, reload }}>
|
|
58
|
+
{children}
|
|
59
|
+
</TypeRegistryContext.Provider>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function useTypeRegistry() {
|
|
64
|
+
const ctx = useContext(TypeRegistryContext);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!ctx.registry && !ctx.isLoading) {
|
|
68
|
+
ctx.load();
|
|
69
|
+
}
|
|
70
|
+
}, [ctx.registry, ctx.isLoading]);
|
|
71
|
+
|
|
72
|
+
return ctx;
|
|
73
|
+
}
|