@parhelia/core 0.1.12774 → 0.1.12776
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/agents-view/AgentsInbox.d.ts +5 -1
- package/dist/agents-view/AgentsInbox.js +5 -3
- package/dist/agents-view/AgentsInbox.js.map +1 -1
- package/dist/agents-view/AgentsView.js +29 -13
- package/dist/agents-view/AgentsView.js.map +1 -1
- package/dist/config/config.js +6 -5
- package/dist/config/config.js.map +1 -1
- package/dist/config/types.d.ts +1 -0
- package/dist/config/types.js.map +1 -1
- package/dist/editor/ai/AgentTerminal.js +85 -12
- package/dist/editor/ai/AgentTerminal.js.map +1 -1
- package/dist/editor/ai/AgentTerminalStatusBar.js +27 -31
- package/dist/editor/ai/AgentTerminalStatusBar.js.map +1 -1
- package/dist/editor/ai/AiResponseMessage.js +2 -2
- package/dist/editor/ai/AiResponseMessage.js.map +1 -1
- package/dist/editor/ai/HeartbeatMessage.js +2 -2
- package/dist/editor/ai/HeartbeatMessage.js.map +1 -1
- package/dist/editor/ai/QueuedPromptsPanel.js +3 -2
- package/dist/editor/ai/QueuedPromptsPanel.js.map +1 -1
- package/dist/editor/ai/SpawnedAgentsPanel.js +2 -2
- package/dist/editor/ai/SpawnedAgentsPanel.js.map +1 -1
- package/dist/editor/ai/TimeWithTooltip.d.ts +9 -0
- package/dist/editor/ai/TimeWithTooltip.js +12 -0
- package/dist/editor/ai/TimeWithTooltip.js.map +1 -0
- package/dist/editor/ai/ToolCallDisplay.js +6 -3
- package/dist/editor/ai/ToolCallDisplay.js.map +1 -1
- package/dist/editor/ai/UserMessage.js +3 -3
- package/dist/editor/ai/UserMessage.js.map +1 -1
- package/dist/editor/ai/agentDiagnostics.js +2 -1
- package/dist/editor/ai/agentDiagnostics.js.map +1 -1
- package/dist/editor/ai/agentDiagnostics.test.js +39 -1
- package/dist/editor/ai/agentDiagnostics.test.js.map +1 -1
- package/dist/editor/ai/agentMessageHelpers.d.ts +1 -0
- package/dist/editor/ai/agentMessageHelpers.js +6 -0
- package/dist/editor/ai/agentMessageHelpers.js.map +1 -1
- package/dist/editor/services/agentService.d.ts +8 -0
- package/dist/editor/services/agentService.js.map +1 -1
- package/dist/editor/services/aiService.d.ts +1 -1
- package/dist/editor/services/aiService.js +3 -2
- package/dist/editor/services/aiService.js.map +1 -1
- package/dist/editor/settings/QuotaInfo.js +29 -60
- package/dist/editor/settings/QuotaInfo.js.map +1 -1
- package/dist/editor/settings/QuotaUserPicker.d.ts +13 -0
- package/dist/editor/settings/QuotaUserPicker.js +58 -0
- package/dist/editor/settings/QuotaUserPicker.js.map +1 -0
- package/dist/editor/settings/SettingsBreadcrumb.d.ts +5 -1
- package/dist/editor/settings/SettingsBreadcrumb.js +3 -3
- package/dist/editor/settings/SettingsBreadcrumb.js.map +1 -1
- package/dist/editor/settings/SettingsHeaderActionsContext.d.ts +7 -1
- package/dist/editor/settings/SettingsHeaderActionsContext.js +22 -0
- package/dist/editor/settings/SettingsHeaderActionsContext.js.map +1 -1
- package/dist/editor/settings/SettingsView.js +17 -14
- package/dist/editor/settings/SettingsView.js.map +1 -1
- package/dist/editor/settings/panels/AgentsPanel.js +2 -4
- package/dist/editor/settings/panels/AgentsPanel.js.map +1 -1
- package/dist/editor/settings/panels/GroupedFieldConfigPanel.js +2 -8
- package/dist/editor/settings/panels/GroupedFieldConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/ItemConfigPanel.js +1 -1
- package/dist/editor/settings/panels/ItemConfigPanel.js.map +1 -1
- package/dist/editor/settings/panels/ModelsPanel.js +28 -14
- package/dist/editor/settings/panels/ModelsPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplateSelector.d.ts +2 -7
- package/dist/editor/settings/panels/ProjectTemplateSelector.js +6 -8
- package/dist/editor/settings/panels/ProjectTemplateSelector.js.map +1 -1
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js +194 -31
- package/dist/editor/settings/panels/ProjectTemplatesPanel.js.map +1 -1
- package/dist/editor/settings/panels/ProviderIcon.d.ts +17 -0
- package/dist/editor/settings/panels/ProviderIcon.js +89 -0
- package/dist/editor/settings/panels/ProviderIcon.js.map +1 -0
- package/dist/editor/settings/panels/ProvidersPanel.js +17 -4
- package/dist/editor/settings/panels/ProvidersPanel.js.map +1 -1
- package/dist/editor/ui/SimpleTabs.js +28 -3
- package/dist/editor/ui/SimpleTabs.js.map +1 -1
- package/dist/editor/ui/Splitter.d.ts +1 -0
- package/dist/editor/ui/Splitter.js +7 -1
- package/dist/editor/ui/Splitter.js.map +1 -1
- package/dist/revision.d.ts +2 -2
- package/dist/revision.js +2 -2
- package/dist/task-board/TaskBoardWorkspace.js +1 -1
- package/dist/task-board/TaskBoardWorkspace.js.map +1 -1
- package/dist/task-board/components/ProjectDashboard.js +3 -3
- package/dist/task-board/components/ProjectDashboard.js.map +1 -1
- package/dist/task-board/components/ProjectPropertiesPanel.js +1 -1
- package/dist/task-board/components/ProjectPropertiesPanel.js.map +1 -1
- package/dist/task-board/views/DependencyGraphView.d.ts +9 -1
- package/dist/task-board/views/DependencyGraphView.js +124 -29
- package/dist/task-board/views/DependencyGraphView.js.map +1 -1
- package/package.json +1 -1
|
@@ -19,6 +19,7 @@ import { deleteProjectTemplate, getProjectTemplates, getProjectTemplateAgent, re
|
|
|
19
19
|
import { getAiProfilesErrorMessage, loadAiProfiles, } from "../../services/aiService";
|
|
20
20
|
import { updateAgentContext } from "../../services/agentService";
|
|
21
21
|
import { useEditContext } from "../../client/editContext";
|
|
22
|
+
import { localStorageService } from "../../services/localStorageService";
|
|
22
23
|
import { ItemNameDialog, } from "../../ui/ItemNameDialogNew";
|
|
23
24
|
import { SimpleTabs } from "../../ui/SimpleTabs";
|
|
24
25
|
import { isTypingEventTarget } from "../../utils/keyboardNavigation";
|
|
@@ -26,10 +27,25 @@ import { cn } from "../../../lib/utils";
|
|
|
26
27
|
import { AgentProfileEditorPanel, } from "./AgentProfileEditorPanel";
|
|
27
28
|
import { ProjectTemplateAgentPanel } from "./ProjectTemplateAgentPanel";
|
|
28
29
|
import { ProjectTemplateSelector } from "./ProjectTemplateSelector";
|
|
29
|
-
import { useRequiredSettingsHeaderActions } from "../SettingsHeaderActionsContext";
|
|
30
|
+
import { useRequiredSettingsBreadcrumbExtra, useRequiredSettingsHeaderActions, useRequiredSettingsMainContentRef, } from "../SettingsHeaderActionsContext";
|
|
30
31
|
import { useSearchParams } from "../../client/navigation";
|
|
31
32
|
// Query param for deep-linking the selected project template (settings reload).
|
|
32
33
|
const SELECTED_PROJECT_TEMPLATE_QUERY_PARAM = "projectTemplateId";
|
|
34
|
+
function measureOverlayBounds(element) {
|
|
35
|
+
if (!element) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const rect = element.getBoundingClientRect();
|
|
39
|
+
if (rect.width <= 0 || rect.height <= 0) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
top: rect.top,
|
|
44
|
+
left: rect.left,
|
|
45
|
+
width: rect.width,
|
|
46
|
+
height: rect.height,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
33
49
|
const PRIORITY_OPTIONS = [
|
|
34
50
|
{ value: "", label: "No priority" },
|
|
35
51
|
{ value: "Low", label: "Low" },
|
|
@@ -51,6 +67,7 @@ const PROJECT_TEMPLATE_ROOT_PATHS = [
|
|
|
51
67
|
"/sitecore/system/parhelia/settings/project templates",
|
|
52
68
|
"/sitecore/system/parhelia/system/project templates",
|
|
53
69
|
];
|
|
70
|
+
const PROJECT_TEMPLATE_SPLITTER_STORAGE_KEY = "settings-project-templates-panel-splitter-v3";
|
|
54
71
|
function isProjectTemplateItemChange(changes, selectedTemplateId) {
|
|
55
72
|
const normalizedSelectedTemplateId = selectedTemplateId?.toLowerCase() ?? null;
|
|
56
73
|
return changes.some((change) => {
|
|
@@ -198,7 +215,7 @@ function buildUpsertTemplateRequest(template) {
|
|
|
198
215
|
disabled: normalizedTask.disabled === true,
|
|
199
216
|
taskType: normalizedTask.taskType ?? "Task",
|
|
200
217
|
decisionMode: normalizedTask.taskType === "DecisionPoint"
|
|
201
|
-
? normalizedTask.decisionMode ?? "MultiSelect"
|
|
218
|
+
? (normalizedTask.decisionMode ?? "MultiSelect")
|
|
202
219
|
: null,
|
|
203
220
|
priority: normalizedTask.priority || null,
|
|
204
221
|
sortOrder: normalizedTask.sortOrder || index * 100,
|
|
@@ -406,7 +423,13 @@ export function ProjectTemplatesPanel() {
|
|
|
406
423
|
const [duplicatingAgentProfile, setDuplicatingAgentProfile] = useState(false);
|
|
407
424
|
const [mobileView, setMobileView] = useState("detail");
|
|
408
425
|
const [editingAgentProfile, setEditingAgentProfile] = useState(null);
|
|
426
|
+
const [isTaskEditorMaximized, setIsTaskEditorMaximized] = useState(false);
|
|
427
|
+
const [taskEditorOverlayBounds, setTaskEditorOverlayBounds] = useState(null);
|
|
428
|
+
const [forceCollapseAgentPanelKey, setForceCollapseAgentPanelKey] = useState(0);
|
|
429
|
+
const [forceExpandAgentPanelKey, setForceExpandAgentPanelKey] = useState(0);
|
|
430
|
+
const settingsMainContentRef = useRequiredSettingsMainContentRef();
|
|
409
431
|
const setSettingsHeaderActions = useRequiredSettingsHeaderActions();
|
|
432
|
+
const setSettingsBreadcrumbExtra = useRequiredSettingsBreadcrumbExtra();
|
|
410
433
|
const autosaveTimeoutRef = useRef(null);
|
|
411
434
|
const lastPersistedSignatureRef = useRef(null);
|
|
412
435
|
const latestDraftSignatureRef = useRef(null);
|
|
@@ -420,6 +443,108 @@ export function ProjectTemplatesPanel() {
|
|
|
420
443
|
const templateAgentLoadRunIdRef = useRef(0);
|
|
421
444
|
const externalRefreshTimeoutRef = useRef(null);
|
|
422
445
|
const pendingExternalRefreshRef = useRef(false);
|
|
446
|
+
const agentPanelWasExpandedBeforeMaximizeRef = useRef(null);
|
|
447
|
+
const exitTaskEditorMaximizedRef = useRef(() => { });
|
|
448
|
+
const wasAgentPanelCollapsed = () => {
|
|
449
|
+
const stored = localStorageService.getItem(PROJECT_TEMPLATE_SPLITTER_STORAGE_KEY);
|
|
450
|
+
return stored?.lastCollapsed ?? true;
|
|
451
|
+
};
|
|
452
|
+
const restoreAgentPanelAfterMaximize = () => {
|
|
453
|
+
if (agentPanelWasExpandedBeforeMaximizeRef.current === true) {
|
|
454
|
+
setForceExpandAgentPanelKey((value) => value + 1);
|
|
455
|
+
}
|
|
456
|
+
agentPanelWasExpandedBeforeMaximizeRef.current = null;
|
|
457
|
+
};
|
|
458
|
+
/* eslint-disable react-hooks/exhaustive-deps */
|
|
459
|
+
const exitTaskEditorMaximized = () => {
|
|
460
|
+
setIsTaskEditorMaximized(false);
|
|
461
|
+
restoreAgentPanelAfterMaximize();
|
|
462
|
+
};
|
|
463
|
+
exitTaskEditorMaximizedRef.current = exitTaskEditorMaximized;
|
|
464
|
+
const handleToggleTaskEditorMaximized = () => {
|
|
465
|
+
if (isTaskEditorMaximized) {
|
|
466
|
+
exitTaskEditorMaximized();
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
const nextBounds = measureOverlayBounds(settingsMainContentRef.current);
|
|
470
|
+
if (!nextBounds) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
const agentPanelCollapsed = wasAgentPanelCollapsed();
|
|
474
|
+
agentPanelWasExpandedBeforeMaximizeRef.current = !agentPanelCollapsed;
|
|
475
|
+
if (!agentPanelCollapsed) {
|
|
476
|
+
setForceCollapseAgentPanelKey((value) => value + 1);
|
|
477
|
+
}
|
|
478
|
+
setTaskEditorOverlayBounds(nextBounds);
|
|
479
|
+
setIsTaskEditorMaximized(true);
|
|
480
|
+
};
|
|
481
|
+
useEffect(() => {
|
|
482
|
+
if (!isTaskEditorMaximized) {
|
|
483
|
+
setTaskEditorOverlayBounds(null);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const settingsMainContent = settingsMainContentRef.current;
|
|
487
|
+
const updateBounds = () => {
|
|
488
|
+
const nextBounds = measureOverlayBounds(settingsMainContentRef.current);
|
|
489
|
+
if (nextBounds) {
|
|
490
|
+
setTaskEditorOverlayBounds(nextBounds);
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
updateBounds();
|
|
494
|
+
if (typeof window === "undefined") {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
let frameId = null;
|
|
498
|
+
const scheduleUpdate = () => {
|
|
499
|
+
if (frameId != null) {
|
|
500
|
+
window.cancelAnimationFrame(frameId);
|
|
501
|
+
}
|
|
502
|
+
frameId = window.requestAnimationFrame(() => {
|
|
503
|
+
frameId = null;
|
|
504
|
+
updateBounds();
|
|
505
|
+
});
|
|
506
|
+
};
|
|
507
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" && settingsMainContent
|
|
508
|
+
? new ResizeObserver(scheduleUpdate)
|
|
509
|
+
: null;
|
|
510
|
+
if (resizeObserver && settingsMainContent) {
|
|
511
|
+
resizeObserver.observe(settingsMainContent);
|
|
512
|
+
}
|
|
513
|
+
window.addEventListener("resize", scheduleUpdate);
|
|
514
|
+
window.addEventListener("scroll", scheduleUpdate, true);
|
|
515
|
+
return () => {
|
|
516
|
+
if (frameId != null) {
|
|
517
|
+
window.cancelAnimationFrame(frameId);
|
|
518
|
+
}
|
|
519
|
+
resizeObserver?.disconnect();
|
|
520
|
+
window.removeEventListener("resize", scheduleUpdate);
|
|
521
|
+
window.removeEventListener("scroll", scheduleUpdate, true);
|
|
522
|
+
};
|
|
523
|
+
}, [isTaskEditorMaximized, settingsMainContentRef]);
|
|
524
|
+
useEffect(() => {
|
|
525
|
+
if (isTaskEditorMaximized &&
|
|
526
|
+
(!draftTemplate || activeDetailTab !== "tasks" || isMobile)) {
|
|
527
|
+
exitTaskEditorMaximizedRef.current();
|
|
528
|
+
}
|
|
529
|
+
}, [
|
|
530
|
+
activeDetailTab,
|
|
531
|
+
draftTemplate,
|
|
532
|
+
isMobile,
|
|
533
|
+
isTaskEditorMaximized,
|
|
534
|
+
]);
|
|
535
|
+
const projectTemplateEditorStyle = useMemo(() => {
|
|
536
|
+
if (!isTaskEditorMaximized || !taskEditorOverlayBounds) {
|
|
537
|
+
return undefined;
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
position: "fixed",
|
|
541
|
+
top: taskEditorOverlayBounds.top,
|
|
542
|
+
left: taskEditorOverlayBounds.left,
|
|
543
|
+
width: taskEditorOverlayBounds.width,
|
|
544
|
+
height: taskEditorOverlayBounds.height,
|
|
545
|
+
zIndex: 80,
|
|
546
|
+
};
|
|
547
|
+
}, [isTaskEditorMaximized, taskEditorOverlayBounds]);
|
|
423
548
|
useEffect(() => {
|
|
424
549
|
let cancelled = false;
|
|
425
550
|
loadAiProfiles()
|
|
@@ -459,8 +584,9 @@ export function ProjectTemplatesPanel() {
|
|
|
459
584
|
return;
|
|
460
585
|
}
|
|
461
586
|
const clonedTemplate = template ? cloneTemplate(template) : null;
|
|
462
|
-
const
|
|
463
|
-
clonedTemplate.id === selectedTemplateIdRef.current
|
|
587
|
+
const isSameTemplate = !!clonedTemplate &&
|
|
588
|
+
clonedTemplate.id === selectedTemplateIdRef.current;
|
|
589
|
+
const preferredTaskId = isSameTemplate
|
|
464
590
|
? selectedTaskIdRef.current
|
|
465
591
|
: null;
|
|
466
592
|
const persistedSignature = clonedTemplate && clonedTemplate.name.trim()
|
|
@@ -472,7 +598,12 @@ export function ProjectTemplatesPanel() {
|
|
|
472
598
|
setDraftTemplate(clonedTemplate);
|
|
473
599
|
setEditingAgentProfile(null);
|
|
474
600
|
setSelectedTaskId(getSelectedTaskIdForTemplate(clonedTemplate, preferredTaskId));
|
|
475
|
-
|
|
601
|
+
// Re-selecting the same template (e.g. via external refresh) must not
|
|
602
|
+
// clobber the user's current tab choice. Only reset the tab when the
|
|
603
|
+
// selected template actually changes.
|
|
604
|
+
if (!isSameTemplate) {
|
|
605
|
+
setActiveDetailTab(clonedTemplate && !clonedTemplate.name.trim() ? "basic" : "tasks");
|
|
606
|
+
}
|
|
476
607
|
setIsDirty(false);
|
|
477
608
|
setSaveError(null);
|
|
478
609
|
lastPersistedSignatureRef.current = persistedSignature;
|
|
@@ -518,7 +649,8 @@ export function ProjectTemplatesPanel() {
|
|
|
518
649
|
? null
|
|
519
650
|
: loadedTemplates[0]?.id || null;
|
|
520
651
|
const nextTemplate = loadedTemplates.find((template) => template.id === nextTemplateId);
|
|
521
|
-
if (options?.preserveDraftIfDirty &&
|
|
652
|
+
if (options?.preserveDraftIfDirty &&
|
|
653
|
+
(savingRef.current || isDirtyRef.current)) {
|
|
522
654
|
return;
|
|
523
655
|
}
|
|
524
656
|
selectTemplate(nextTemplate ?? null);
|
|
@@ -618,12 +750,12 @@ export function ProjectTemplatesPanel() {
|
|
|
618
750
|
}, [dependencyTasks, selectedDependencyId]);
|
|
619
751
|
const headerStatusBadge = useMemo(() => {
|
|
620
752
|
if (saveError) {
|
|
621
|
-
return (_jsx("span", { "data-testid": "project-template-save-status", "data-state": "error", className: "inline-flex min-w-[84px] justify-center rounded bg-red-100 px-1.5
|
|
753
|
+
return (_jsx("span", { "data-testid": "project-template-save-status", "data-state": "error", className: "inline-flex min-w-[84px] justify-center rounded bg-red-100 px-1.5 whitespace-nowrap text-red-700", children: "Save failed" }));
|
|
622
754
|
}
|
|
623
755
|
if (isDirty || saving) {
|
|
624
|
-
return (_jsx("span", { "data-testid": "project-template-save-status", "data-state": "saving", className: "inline-flex min-w-[84px] justify-center rounded bg-amber-100 px-1.5
|
|
756
|
+
return (_jsx("span", { "data-testid": "project-template-save-status", "data-state": "saving", className: "inline-flex min-w-[84px] justify-center rounded bg-amber-100 px-1.5 whitespace-nowrap text-amber-700", children: "Saving..." }));
|
|
625
757
|
}
|
|
626
|
-
return (_jsx("span", { "aria-hidden": "true", "data-testid": "project-template-save-status", "data-state": "saved", className: "pointer-events-none inline-flex min-w-[84px] justify-center rounded px-1.5
|
|
758
|
+
return (_jsx("span", { "aria-hidden": "true", "data-testid": "project-template-save-status", "data-state": "saved", className: "pointer-events-none inline-flex min-w-[84px] justify-center rounded px-1.5 whitespace-nowrap opacity-0 select-none", children: "Saving..." }));
|
|
627
759
|
}, [isDirty, saveError, saving]);
|
|
628
760
|
const graphTasks = useMemo(() => buildTemplateTaskItems(draftTemplate, aiProfilesById), [aiProfilesById, draftTemplate]);
|
|
629
761
|
const graphDependencies = useMemo(() => buildTemplateDependencies(draftTemplate), [draftTemplate]);
|
|
@@ -897,7 +1029,9 @@ export function ProjectTemplatesPanel() {
|
|
|
897
1029
|
: null;
|
|
898
1030
|
draftTemplateRef.current = nextTemplate;
|
|
899
1031
|
latestDraftSignatureRef.current = nextSignature;
|
|
900
|
-
setTemplates((existingTemplates) => existingTemplates.map((template) => template.id === nextTemplate.id
|
|
1032
|
+
setTemplates((existingTemplates) => existingTemplates.map((template) => template.id === nextTemplate.id
|
|
1033
|
+
? cloneTemplate(nextTemplate)
|
|
1034
|
+
: template));
|
|
901
1035
|
return nextTemplate;
|
|
902
1036
|
});
|
|
903
1037
|
setIsDirty(true);
|
|
@@ -1527,6 +1661,12 @@ export function ProjectTemplatesPanel() {
|
|
|
1527
1661
|
removeTaskTemplateById(selectedTaskId);
|
|
1528
1662
|
}, [removeTaskTemplateById, selectedTaskId]);
|
|
1529
1663
|
const handleProjectTemplateEditorKeyDownCapture = useCallback((event) => {
|
|
1664
|
+
if (event.key === "Escape" && isTaskEditorMaximized) {
|
|
1665
|
+
event.preventDefault();
|
|
1666
|
+
event.stopPropagation();
|
|
1667
|
+
exitTaskEditorMaximizedRef.current();
|
|
1668
|
+
return;
|
|
1669
|
+
}
|
|
1530
1670
|
if (event.key !== "Delete")
|
|
1531
1671
|
return;
|
|
1532
1672
|
if (isTypingEventTarget(event.nativeEvent))
|
|
@@ -1546,36 +1686,55 @@ export function ProjectTemplatesPanel() {
|
|
|
1546
1686
|
}, [
|
|
1547
1687
|
handleDeleteSelectedTask,
|
|
1548
1688
|
handleRemoveDependency,
|
|
1689
|
+
isTaskEditorMaximized,
|
|
1549
1690
|
selectedDependencyId,
|
|
1550
1691
|
selectedTaskId,
|
|
1551
1692
|
]);
|
|
1552
|
-
const templateSelector = useMemo(() => (_jsx(ProjectTemplateSelector, { templates: templates, selectedTemplate: draftTemplate, selectedTemplateId: selectedTemplateId,
|
|
1693
|
+
const templateSelector = useMemo(() => (_jsx(ProjectTemplateSelector, { templates: templates, selectedTemplate: draftTemplate, selectedTemplateId: selectedTemplateId, error: error, deleting: deleting, loading: state === "loading", onSelectTemplate: handleSelectTemplate, onDeleteTemplate: handleDeleteTemplate })), [
|
|
1553
1694
|
deleting,
|
|
1554
1695
|
draftTemplate,
|
|
1555
1696
|
error,
|
|
1556
|
-
handleCreateTemplate,
|
|
1557
1697
|
handleDeleteTemplate,
|
|
1558
|
-
handleRefreshTemplates,
|
|
1559
1698
|
handleSelectTemplate,
|
|
1560
|
-
headerStatusBadge,
|
|
1561
1699
|
selectedTemplateId,
|
|
1562
1700
|
state,
|
|
1563
1701
|
templates,
|
|
1564
1702
|
]);
|
|
1565
|
-
const
|
|
1703
|
+
const desktopBreadcrumbSelector = useMemo(() => !isMobile ? (_jsx("div", { className: "max-w-[32rem] min-w-0", children: templateSelector })) : null, [isMobile, templateSelector]);
|
|
1704
|
+
const desktopHeaderActions = useMemo(() => {
|
|
1705
|
+
if (isMobile)
|
|
1706
|
+
return null;
|
|
1707
|
+
const showDelete = draftTemplate && !draftTemplate.isSystem;
|
|
1708
|
+
return (_jsxs("div", { className: "flex items-center gap-2", children: [headerStatusBadge, _jsxs(Button, { size: "sm", onClick: () => void handleCreateTemplate(), "data-testid": "project-template-create-button", children: [_jsx(Plus, { className: "h-4 w-4", strokeWidth: 1.5 }), "Create new template"] }), showDelete ? (_jsxs(Button, { variant: "outline", size: "sm", onClick: () => handleDeleteTemplate(), disabled: deleting, "data-testid": "project-template-delete-button", children: [_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 }), deleting ? "Deleting..." : "Delete"] })) : null] }));
|
|
1709
|
+
}, [
|
|
1710
|
+
deleting,
|
|
1711
|
+
draftTemplate,
|
|
1712
|
+
handleCreateTemplate,
|
|
1713
|
+
handleDeleteTemplate,
|
|
1714
|
+
headerStatusBadge,
|
|
1715
|
+
isMobile,
|
|
1716
|
+
]);
|
|
1717
|
+
useEffect(() => {
|
|
1718
|
+
if (!setSettingsBreadcrumbExtra)
|
|
1719
|
+
return;
|
|
1720
|
+
setSettingsBreadcrumbExtra(desktopBreadcrumbSelector);
|
|
1721
|
+
return () => {
|
|
1722
|
+
setSettingsBreadcrumbExtra(null);
|
|
1723
|
+
};
|
|
1724
|
+
}, [desktopBreadcrumbSelector, setSettingsBreadcrumbExtra]);
|
|
1566
1725
|
useEffect(() => {
|
|
1567
1726
|
if (!setSettingsHeaderActions)
|
|
1568
1727
|
return;
|
|
1569
|
-
setSettingsHeaderActions(
|
|
1728
|
+
setSettingsHeaderActions(desktopHeaderActions);
|
|
1570
1729
|
return () => {
|
|
1571
1730
|
setSettingsHeaderActions(null);
|
|
1572
1731
|
};
|
|
1573
|
-
}, [
|
|
1732
|
+
}, [desktopHeaderActions, setSettingsHeaderActions]);
|
|
1574
1733
|
const renderTemplateTaskEditorMain = () => {
|
|
1575
1734
|
if (!draftTemplate) {
|
|
1576
1735
|
return null;
|
|
1577
1736
|
}
|
|
1578
|
-
const graphView = (_jsx("div", { className: "h-full min-h-0 overflow-hidden bg-slate-50/40", "data-testid": "project-template-graph", children: _jsx(DependencyGraphView, { projectId: draftTemplate.id, layoutKey: `project-template:${draftTemplate.id}`, tasks: graphTasks, dependencies: graphDependencies, miniMapWidth:
|
|
1737
|
+
const graphView = (_jsx("div", { className: "h-full min-h-0 overflow-hidden bg-slate-50/40", "data-testid": "project-template-graph", children: _jsx(DependencyGraphView, { projectId: draftTemplate.id, layoutKey: `project-template:${draftTemplate.id}`, tasks: graphTasks, dependencies: graphDependencies, miniMapWidth: 160, miniMapHeight: 120, showMiniMap: !isMobile, isMaximized: !isMobile && isTaskEditorMaximized, onToggleMaximized: !isMobile ? handleToggleTaskEditorMaximized : undefined, orientation: "horizontal", showOrientationToggle: true, autoLayoutStrategy: "hierarchy", savedLayout: draftTemplate.graphLayout, selectedTaskId: selectedTaskId, selectedDependencyId: selectedDependencyId, onSelectTask: handleSelectTask, onSelectDependency: handleSelectDependencyFromGraph, onClearDependencySelection: handleClearDependencySelection, onAddDependentTaskFromNode: (taskId) => {
|
|
1579
1738
|
void handleOpenCreateDependentTaskDialogForTask(taskId);
|
|
1580
1739
|
}, onAddChildTaskFromNode: (taskId) => {
|
|
1581
1740
|
void handleOpenAddChildTaskDialogForTask(taskId);
|
|
@@ -1590,7 +1749,7 @@ export function ProjectTemplatesPanel() {
|
|
|
1590
1749
|
}));
|
|
1591
1750
|
return nextLayout;
|
|
1592
1751
|
} }) }));
|
|
1593
|
-
const taskPane = (_jsx("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", "data-testid": "project-template-task-detail-pane", children: !selectedTask ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: _jsx("div", { className: "rounded-lg border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-xs text-gray-500", children: "Select a task node to edit its details." }) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "shrink-0 border-b border-gray-100 bg-white px-4 pt-4 pb-3", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-xs font-semibold text-gray-900", "data-testid": "project-template-selected-task-title", children: selectedTask.title || "Untitled Task" }), selectedTask.taskType === "DecisionPoint" ? (_jsx(Badge, { variant: "outline", className: "border-violet-200 bg-violet-50 text-[10px]
|
|
1752
|
+
const taskPane = (_jsx("div", { className: "flex h-full min-h-0 flex-col overflow-hidden", "data-testid": "project-template-task-detail-pane", children: !selectedTask ? (_jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: _jsx("div", { className: "rounded-lg border border-dashed border-gray-200 bg-gray-50 p-6 text-center text-xs text-gray-500", children: "Select a task node to edit its details." }) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "shrink-0 border-b border-gray-100 bg-white px-4 pt-4 pb-3", children: _jsxs("div", { className: "flex items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-xs font-semibold text-gray-900", "data-testid": "project-template-selected-task-title", children: selectedTask.title || "Untitled Task" }), selectedTask.taskType === "DecisionPoint" ? (_jsx(Badge, { variant: "outline", className: "border-violet-200 bg-violet-50 text-[10px] text-violet-700 uppercase", children: "Decision Point" })) : null, selectedTask.disabled ? (_jsx(Badge, { variant: "outline", className: "text-[10px] uppercase", children: "Disabled" })) : null] }), _jsx("div", { className: "text-xs text-gray-500", children: "Configure task details, assignment, and dependencies." })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleDeleteSelectedTask, "data-testid": "project-template-remove-task-button", children: [_jsx(Trash2, { className: "h-4 w-4", strokeWidth: 1.5 }), "Remove Task"] })] }) }), _jsx("div", { className: "min-h-0 flex-1 overflow-y-auto px-4 pt-3 pb-4", children: _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", htmlFor: "project-template-task-title-input", children: "Task title" }), _jsx(Input, { id: "project-template-task-title-input", value: selectedTask.title, onChange: (event) => updateSelectedTask((currentTask) => ({
|
|
1594
1753
|
...currentTask,
|
|
1595
1754
|
title: event.target.value,
|
|
1596
1755
|
})), placeholder: "Task title", className: "text-xs md:text-xs", "data-testid": "project-template-task-title-input" })] }), _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Description" }), _jsx(Textarea, { className: "text-xs", value: selectedTask.description ?? "", onChange: (event) => updateSelectedTask((currentTask) => ({
|
|
@@ -1598,9 +1757,11 @@ export function ProjectTemplatesPanel() {
|
|
|
1598
1757
|
description: event.target.value,
|
|
1599
1758
|
})), rows: 4, placeholder: "Describe what this task should accomplish", "data-testid": "project-template-task-description-input" })] }), _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Task type" }), _jsx(Select, { className: "text-xs", value: selectedTask.taskType ?? "Task", onValueChange: (value) => updateSelectedTask((currentTask) => ({
|
|
1600
1759
|
...currentTask,
|
|
1601
|
-
taskType: value === "DecisionPoint"
|
|
1760
|
+
taskType: value === "DecisionPoint"
|
|
1761
|
+
? "DecisionPoint"
|
|
1762
|
+
: "Task",
|
|
1602
1763
|
decisionMode: value === "DecisionPoint"
|
|
1603
|
-
? currentTask.decisionMode ?? "MultiSelect"
|
|
1764
|
+
? (currentTask.decisionMode ?? "MultiSelect")
|
|
1604
1765
|
: null,
|
|
1605
1766
|
})), options: TASK_TYPE_OPTIONS, "data-testid": "project-template-task-type-select" })] }), selectedTask.taskType === "DecisionPoint" ? (_jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Decision mode" }), _jsx(Select, { className: "text-xs", value: selectedTask.decisionMode ?? "MultiSelect", onValueChange: (value) => updateSelectedTask((currentTask) => ({
|
|
1606
1767
|
...currentTask,
|
|
@@ -1610,14 +1771,14 @@ export function ProjectTemplatesPanel() {
|
|
|
1610
1771
|
})), options: DECISION_MODE_OPTIONS, "data-testid": "project-template-task-decision-mode-select" })] })) : null, _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { className: "text-xs", children: "Priority" }), _jsx(Select, { className: "text-xs", value: selectedTask.priority ?? "", onValueChange: (value) => updateSelectedTask((currentTask) => ({
|
|
1611
1772
|
...currentTask,
|
|
1612
1773
|
priority: value || null,
|
|
1613
|
-
})), options: PRIORITY_OPTIONS, "data-testid": "project-template-task-priority-select" })] }), selectedTask.taskType === "DecisionPoint" ? (_jsx("div", { className: "grid gap-1.5 md:col-span-2", children: _jsx("div", { className: "rounded-md border border-violet-100 bg-violet-50 px-3 py-2 text-[11px] leading-snug text-violet-800", children: "Direct dependents of this task become selectable option roots when the project is created. Their descendants are materialized only after the Decision Point is resolved at runtime." }) })) : null, _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1774
|
+
})), options: PRIORITY_OPTIONS, "data-testid": "project-template-task-priority-select" })] }), selectedTask.taskType === "DecisionPoint" ? (_jsx("div", { className: "grid gap-1.5 md:col-span-2", children: _jsx("div", { className: "rounded-md border border-violet-100 bg-violet-50 px-3 py-2 text-[11px] leading-snug text-violet-800", children: "Direct dependents of this task become selectable option roots when the project is created. Their descendants are materialized only after the Decision Point is resolved at runtime." }) })) : null, _jsxs("div", { className: "grid gap-1.5 md:col-span-2", children: [_jsx(Label, { className: "text-xs", children: "Default assignee" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsx(TaskAssigneePicker, { projectTemplateId: draftTemplate.id, assigneeType: selectedTask.assigneeType ?? null, value: selectedTask.assigneeId ?? null, displayValue: selectedTaskAssigneeDisplayValue, onChange: (next) => updateSelectedTask((currentTask) => ({
|
|
1775
|
+
...currentTask,
|
|
1776
|
+
assigneeType: next?.assigneeType ?? null,
|
|
1777
|
+
assigneeId: next?.assigneeId ?? null,
|
|
1778
|
+
agentProfileId: next?.assigneeType === "Agent"
|
|
1779
|
+
? next.assigneeId
|
|
1780
|
+
: null,
|
|
1781
|
+
})), placeholder: "Assign user or agent", buttonClassName: "h-[27px] w-full justify-between rounded-md bg-gray-5 text-xs", buttonTestId: "project-template-task-assignee-picker" }) }), selectedTask.assigneeType === "Agent" ? (_jsxs(Button, { type: "button", size: "sm", variant: "outline", className: "h-[27px] shrink-0 gap-1 px-2 text-xs", onClick: () => void handleEditAssignedAgentProfile(), disabled: !selectedAgentProfileId, "data-testid": "project-template-task-edit-agent-profile-button", children: [_jsx(Settings2, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }), "Edit Profile"] })) : null] }), profilesError ? (_jsx("div", { className: "text-xs text-amber-700", children: profilesError })) : null] })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", "data-testid": "project-template-dependencies-section", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800", children: [_jsx(Shield, { className: "h-4 w-4 text-gray-500", strokeWidth: 1.5 }), "Dependencies"] }), _jsxs(Popover, { open: dependencyPickerOpen, onOpenChange: setDependencyPickerOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: _jsx(Button, { type: "button", size: "sm", variant: "outline", className: "h-7 gap-1 px-2", disabled: dependencyAddCandidates.length === 0, "aria-label": "Add dependency", "data-testid": "project-template-add-dependency-button", children: _jsx(Plus, { className: "h-3.5 w-3.5", strokeWidth: 1.5 }) }) }), _jsxs(PopoverContent, { align: "end", className: "w-72 p-0", sideOffset: 6, "data-testid": "project-template-dependency-popover", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-2 text-xs font-medium text-gray-700", children: "Depends on" }), _jsx("div", { className: "max-h-56 overflow-y-auto p-1", children: dependencyAddCandidates.length === 0 ? (_jsx("div", { className: "px-2 py-3 text-center text-xs text-gray-500", children: "All other tasks are already linked." })) : (dependencyAddCandidates.map((task) => (_jsxs("button", { type: "button", className: "flex w-full items-center gap-2 rounded-md px-2 py-2 text-left text-xs hover:bg-gray-100", onClick: () => handleAddDependency(task.id), "data-testid": `project-template-dependency-option-${task.id}`, children: [_jsx(Link2, { className: "h-3.5 w-3.5 shrink-0 text-gray-400", strokeWidth: 1.5 }), _jsx("span", { className: "min-w-0 truncate font-medium text-gray-900", children: task.title || "Untitled Task" })] }, task.id)))) })] })] })] }), _jsx("p", { className: "mb-2 text-[11px] leading-snug text-gray-500", children: "This task must wait for these tasks to finish first." }), dependencyTasks.length === 0 ? (_jsx("div", { className: "text-xs text-gray-500", children: draftTemplate.taskTemplates.filter((t) => t.id !== selectedTask.id).length === 0
|
|
1621
1782
|
? "Add more task templates to create dependencies."
|
|
1622
1783
|
: "No dependencies yet. Use + to add one." })) : (_jsx("ul", { className: "space-y-1.5", children: dependencyTasks.map((task) => (_jsxs("li", { tabIndex: 0, className: cn("flex items-center justify-between gap-2 rounded-md border bg-white px-3 py-2 text-xs transition-colors outline-none", selectedDependencyId === task.id
|
|
1623
1784
|
? "border-amber-300 bg-amber-50 ring-1 ring-amber-200"
|
|
@@ -1693,7 +1854,9 @@ export function ProjectTemplatesPanel() {
|
|
|
1693
1854
|
openInWizardMode: checked,
|
|
1694
1855
|
})), "aria-label": "Open new projects from this template in wizard mode", "data-testid": "project-template-open-in-wizard-mode-switch" })] })] }));
|
|
1695
1856
|
const taskEditorContent = (_jsx("div", { className: "flex h-full min-h-0 flex-1 flex-col overflow-hidden", children: renderTemplateTaskEditorMain() }));
|
|
1696
|
-
return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-y-contain
|
|
1857
|
+
return (_jsxs("div", { className: cn("flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-y-contain md:overflow-hidden", isTaskEditorMaximized ? "overflow-hidden p-0" : "p-5"), children: [_jsx("div", { className: "flex shrink-0 flex-col gap-5 md:max-h-[50vh] md:min-h-0 md:overflow-y-auto md:overscroll-y-contain", children: saveError && (_jsx("div", { className: "rounded border border-red-200 bg-red-50 px-4 py-3 text-xs text-red-700", children: saveError })) }), _jsxs("section", { className: cn("flex min-h-0 flex-1 flex-col rounded-lg border border-gray-200 bg-white shadow-sm md:overflow-hidden", isTaskEditorMaximized && "overflow-hidden rounded-none shadow-xl"), "data-testid": "project-template-task-editor-shell", children: [_jsxs("div", { className: "flex shrink-0 items-center justify-between gap-3 border-b border-gray-100 px-4 py-3", children: [_jsx("div", { className: "min-w-0 flex-1", children: _jsx(SimpleTabs, { tabs: detailTabs, activeTab: activeDetailTab === "basic" ? 0 : 1, setActiveTab: (index) => setActiveDetailTab(index === 0 ? "basic" : "tasks"), hideContent: true, variant: "elegant", className: "w-full justify-start gap-6" }) }), _jsxs(Button, { size: "sm", variant: "outline", onClick: () => void handleOpenCreateTaskDialog(), disabled: creatingTask, "data-testid": "project-template-add-task-button", className: activeDetailTab === "tasks"
|
|
1858
|
+
? ""
|
|
1859
|
+
: "pointer-events-none invisible", "aria-hidden": activeDetailTab !== "tasks", tabIndex: activeDetailTab === "tasks" ? undefined : -1, children: [_jsx(Plus, { className: "h-4 w-4", strokeWidth: 1.5 }), "Add Task"] })] }), _jsx("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden", children: activeDetailTab === "basic"
|
|
1697
1860
|
? basicSettingsContent
|
|
1698
1861
|
: taskEditorContent })] })] }));
|
|
1699
1862
|
})()) : (_jsx("div", { className: "flex min-h-0 flex-1 items-center justify-center p-5", children: _jsxs("div", { className: "max-w-sm rounded-lg border border-dashed border-gray-300 bg-white p-8 text-center", children: [_jsx(GitBranch, { className: "mx-auto mb-3 h-10 w-10 text-gray-300", strokeWidth: 1.2 }), _jsx("h3", { className: "text-xs font-semibold text-gray-900", children: "Select a project template" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Choose a template from the dropdown or create a new one to start editing." })] }) }))] }));
|
|
@@ -1719,7 +1882,7 @@ export function ProjectTemplatesPanel() {
|
|
|
1719
1882
|
hidden: isMobile && mobileView !== "agent",
|
|
1720
1883
|
},
|
|
1721
1884
|
];
|
|
1722
|
-
return (_jsxs(_Fragment, { children: [_jsx("div", { className: "h-full", "data-testid": "project-template-editor", onKeyDownCapture: handleProjectTemplateEditorKeyDownCapture, children: _jsx(Splitter, { panels: panels, localStorageKey:
|
|
1885
|
+
return (_jsxs(_Fragment, { children: [_jsx("div", { className: cn("h-full", isTaskEditorMaximized && "fixed overflow-hidden bg-white shadow-xl"), style: projectTemplateEditorStyle, "data-testid": "project-template-editor", onKeyDownCapture: handleProjectTemplateEditorKeyDownCapture, children: _jsx(Splitter, { panels: panels, localStorageKey: PROJECT_TEMPLATE_SPLITTER_STORAGE_KEY, direction: "horizontal", className: "h-full", forceExpandLastPanelKey: editingAgentProfile?.id ?? null }) }), _jsx(Dialog, { open: isCreateTaskDialogOpen, onOpenChange: (open) => {
|
|
1723
1886
|
setIsCreateTaskDialogOpen(open);
|
|
1724
1887
|
if (!open) {
|
|
1725
1888
|
setNewTaskDependencySourceId(null);
|