fixes doc generation and renderers
All checks were successful
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m23s
All checks were successful
Deploy Nyla Frontend to Integration / deploy (push) Successful in 1m23s
This commit is contained in:
parent
7876a528f5
commit
59b1e1f6a7
4 changed files with 51 additions and 30 deletions
|
|
@ -40,6 +40,8 @@ export const ChatStream: React.FC<ChatStreamProps> = ({ messages,
|
|||
}) => {
|
||||
const { t } = useLanguage();
|
||||
const bottomRef = useRef<HTMLDivElement>(null);
|
||||
const scrollContainerRef = useRef<HTMLDivElement>(null);
|
||||
const isAtBottomRef = useRef(true);
|
||||
const audioQueue = useAudioQueue();
|
||||
const enqueuedIdsRef = useRef<Set<string>>(new Set());
|
||||
const [copiedId, setCopiedId] = useState<string | null>(null);
|
||||
|
|
@ -51,8 +53,17 @@ export const ChatStream: React.FC<ChatStreamProps> = ({ messages,
|
|||
}).catch(() => {});
|
||||
}, []);
|
||||
|
||||
const _handleScroll = useCallback(() => {
|
||||
const el = scrollContainerRef.current;
|
||||
if (!el) return;
|
||||
const threshold = 80;
|
||||
isAtBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < threshold;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
if (isAtBottomRef.current) {
|
||||
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
}, [messages, agentProgress]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -71,15 +82,19 @@ export const ChatStream: React.FC<ChatStreamProps> = ({ messages,
|
|||
}, [messages, audioQueue]);
|
||||
|
||||
return (
|
||||
<div style={{
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
overflowY: 'auto',
|
||||
padding: '16px 24px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 12,
|
||||
}}>
|
||||
<div
|
||||
ref={scrollContainerRef}
|
||||
onScroll={_handleScroll}
|
||||
style={{
|
||||
flex: 1,
|
||||
minHeight: 0,
|
||||
overflowY: 'auto',
|
||||
padding: '16px 24px',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: 12,
|
||||
}}
|
||||
>
|
||||
{messages.map((msg) => (
|
||||
<div
|
||||
key={msg.id}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ interface WorkspaceInputProps {
|
|||
onFeatureSourceDrop?: (params: { featureInstanceId: string; featureCode: string; tableName?: string; objectKey: string; label: string; fieldName?: string }) => void;
|
||||
onDataSourceDrop?: (params: { connectionId: string; sourceType: string; path: string; label: string; displayPath?: string }) => void;
|
||||
pendingAttachDsId?: string;
|
||||
pendingAttachDsLabel?: string;
|
||||
onPendingAttachDsConsumed?: () => void;
|
||||
pendingAttachFdsId?: string;
|
||||
onPendingAttachFdsConsumed?: () => void;
|
||||
|
|
@ -124,6 +125,7 @@ export const WorkspaceInput = forwardRef<WorkspaceInputHandle, WorkspaceInputPro
|
|||
onFeatureSourceDrop,
|
||||
onDataSourceDrop,
|
||||
pendingAttachDsId,
|
||||
pendingAttachDsLabel,
|
||||
onPendingAttachDsConsumed,
|
||||
pendingAttachFdsId,
|
||||
onPendingAttachFdsConsumed,
|
||||
|
|
@ -151,6 +153,7 @@ export const WorkspaceInput = forwardRef<WorkspaceInputHandle, WorkspaceInputPro
|
|||
const [attachedFeatureDataSourceIds, setAttachedFeatureDataSourceIds] = useState<string[]>([]);
|
||||
const [neutralizeActive, setNeutralizeActive] = useState(false);
|
||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||
const dsLabelCache = useRef<Map<string, string>>(new Map());
|
||||
|
||||
const _appendAttachment = useCallback((item: AttachmentItem) => {
|
||||
setAttachments(prev => prev.some(a => a.id === item.id) ? prev : [...prev, item]);
|
||||
|
|
@ -182,6 +185,9 @@ export const WorkspaceInput = forwardRef<WorkspaceInputHandle, WorkspaceInputPro
|
|||
|
||||
useEffect(() => {
|
||||
if (!pendingAttachDsId) return;
|
||||
if (pendingAttachDsLabel) {
|
||||
dsLabelCache.current.set(pendingAttachDsId, pendingAttachDsLabel);
|
||||
}
|
||||
setAttachedDataSourceIds(prev => {
|
||||
if (prev.includes(pendingAttachDsId)) return prev;
|
||||
const next = [...prev, pendingAttachDsId];
|
||||
|
|
@ -189,7 +195,7 @@ export const WorkspaceInput = forwardRef<WorkspaceInputHandle, WorkspaceInputPro
|
|||
return next;
|
||||
});
|
||||
onPendingAttachDsConsumed?.();
|
||||
}, [pendingAttachDsId, onPendingAttachDsConsumed, _persistAttachments, attachedFeatureDataSourceIds]);
|
||||
}, [pendingAttachDsId, pendingAttachDsLabel, onPendingAttachDsConsumed, _persistAttachments, attachedFeatureDataSourceIds]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!pendingAttachFdsId) return;
|
||||
|
|
@ -657,7 +663,7 @@ export const WorkspaceInput = forwardRef<WorkspaceInputHandle, WorkspaceInputPro
|
|||
background: '#e8f5e9', color: '#2e7d32', fontWeight: 500,
|
||||
}}
|
||||
>
|
||||
🔗 {ds?.label || ds?.path || dsId}
|
||||
🔗 {ds?.label || ds?.path || dsLabelCache.current.get(dsId) || dsId}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => _removeAttachedDataSource(dsId)}
|
||||
|
|
|
|||
|
|
@ -332,16 +332,13 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
|||
objectKey: params.objectKey,
|
||||
label: params.label,
|
||||
});
|
||||
// Backend response shape parity with /datasources — accept either a flat
|
||||
// ``id`` or a wrapped ``featureDataSource.id`` so a future API tweak
|
||||
// doesn't silently break the chip again.
|
||||
const newId =
|
||||
res.data?.id ||
|
||||
res.data?.featureDataSource?.id ||
|
||||
res.data?.dataSource?.id ||
|
||||
'';
|
||||
workspace.refreshFeatureDataSources();
|
||||
if (newId) {
|
||||
await workspace.refreshFeatureDataSources();
|
||||
setPendingAttachFdsId(newId);
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
@ -350,9 +347,10 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
|||
}, [instanceId, workspace]);
|
||||
|
||||
const [pendingAttachDsId, setPendingAttachDsId] = useState<string>('');
|
||||
const _handleAttachDataSource = useCallback((dsId: string) => {
|
||||
const [pendingAttachDsLabel, setPendingAttachDsLabel] = useState<string>('');
|
||||
const _handleAttachDataSource = useCallback(async (dsId: string) => {
|
||||
await workspace.refreshDataSources();
|
||||
setPendingAttachDsId(dsId);
|
||||
workspace.refreshDataSources();
|
||||
}, [workspace]);
|
||||
|
||||
const _handleDataSourceDrop = useCallback(async (params: { connectionId: string; sourceType: string; path: string; label: string; displayPath?: string }) => {
|
||||
|
|
@ -366,8 +364,9 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
|||
});
|
||||
const newId = res.data?.id || res.data?.dataSource?.id;
|
||||
if (newId) {
|
||||
setPendingAttachDsLabel(params.label || params.displayPath || '');
|
||||
await workspace.refreshDataSources();
|
||||
setPendingAttachDsId(newId);
|
||||
workspace.refreshDataSources();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Failed to drop data source to chat:', err);
|
||||
|
|
@ -562,7 +561,8 @@ export const WorkspacePage: React.FC<WorkspacePageProps> = ({ persistentInstance
|
|||
onFeatureSourceDrop={_handleSendToChat_FeatureSource}
|
||||
onDataSourceDrop={_handleDataSourceDrop}
|
||||
pendingAttachDsId={pendingAttachDsId}
|
||||
onPendingAttachDsConsumed={() => setPendingAttachDsId('')}
|
||||
pendingAttachDsLabel={pendingAttachDsLabel}
|
||||
onPendingAttachDsConsumed={() => { setPendingAttachDsId(''); setPendingAttachDsLabel(''); }}
|
||||
pendingAttachFdsId={pendingAttachFdsId}
|
||||
onPendingAttachFdsConsumed={() => setPendingAttachFdsId('')}
|
||||
onPasteAsFile={_uploadAndAttach}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ interface UseWorkspaceReturn {
|
|||
files: WorkspaceFile[];
|
||||
dataSources: DataSource[];
|
||||
featureDataSources: FeatureDataSource[];
|
||||
refreshFeatureDataSources: () => void;
|
||||
refreshFeatureDataSources: () => Promise<void>;
|
||||
agentProgress: AgentProgress | null;
|
||||
toolActivities: ToolActivity[];
|
||||
pendingEdits: FileEditProposal[];
|
||||
|
|
@ -106,7 +106,7 @@ interface UseWorkspaceReturn {
|
|||
workflowId: string | null;
|
||||
workflowVersion: number;
|
||||
refreshFiles: () => void;
|
||||
refreshDataSources: () => void;
|
||||
refreshDataSources: () => Promise<void>;
|
||||
dataSourceAccesses: DataSourceAccessEvent[];
|
||||
/**
|
||||
* Hydrated chip-bar state for the WorkspaceInput. Set by ``loadWorkflow``
|
||||
|
|
@ -148,18 +148,18 @@ export function useWorkspace(instanceId: string): UseWorkspaceReturn {
|
|||
.catch(err => console.error('Failed to load workspace files:', err));
|
||||
}, [instanceId]);
|
||||
|
||||
const refreshDataSources = useCallback(() => {
|
||||
if (!instanceId) return;
|
||||
api.get(`/api/workspace/${instanceId}/datasources`)
|
||||
const refreshDataSources = useCallback((): Promise<void> => {
|
||||
if (!instanceId) return Promise.resolve();
|
||||
return api.get(`/api/workspace/${instanceId}/datasources`)
|
||||
.then(res => setDataSources(res.data.dataSources || []))
|
||||
.catch(() => {});
|
||||
.catch(err => console.error('Failed to load data sources:', err));
|
||||
}, [instanceId]);
|
||||
|
||||
const refreshFeatureDataSources = useCallback(() => {
|
||||
if (!instanceId) return;
|
||||
api.get(`/api/workspace/${instanceId}/feature-datasources`)
|
||||
const refreshFeatureDataSources = useCallback((): Promise<void> => {
|
||||
if (!instanceId) return Promise.resolve();
|
||||
return api.get(`/api/workspace/${instanceId}/feature-datasources`)
|
||||
.then(res => setFeatureDataSources(res.data.featureDataSources || []))
|
||||
.catch(() => {});
|
||||
.catch(err => console.error('Failed to load feature data sources:', err));
|
||||
}, [instanceId]);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
Loading…
Reference in a new issue