@taskforcehq/taskforce 0.3.300 → 0.3.301
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/README.md +19 -0
- package/dist/components/views/StandaloneLayout.js +24 -3
- package/dist/components/views/panels/FilterToolbar.d.ts +2 -0
- package/dist/components/views/panels/FilterToolbarControls.d.ts +1 -0
- package/dist/components/views/panels/FilterToolbarControls.js +1 -1
- package/dist/components/views/panels/PlanningDrawer.js +1 -1
- package/dist/hooks/useTaskData.js +13 -9
- package/dist/hooks/useTaskData.test.js +61 -0
- package/dist/mcp/runtime.js +4 -4
- package/dist/mcp/runtime.test.js +7 -1
- package/dist/ui/assets/{AgentsModule-TVmCgizS.js → AgentsModule-Bdq3T1Ts.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CAyhnPeR.js → AnnotatedAttachmentWorkspace-jKUVo-PJ.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-BOCw5e4D.js → DocumentWorkspace-DuQa-uIC.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-CN0r02Oi.js → InitiativesModule-C8Pi2VTk.js} +1 -1
- package/dist/ui/assets/{PlansPage-FcQSNLZd.js → PlansPage-B6BDECSi.js} +1 -1
- package/dist/ui/assets/{TaskSettings-Cw5zYD1L.js → TaskSettings-DUJgRTPU.js} +1 -1
- package/dist/ui/assets/{WorkflowsModule-CY9R2zPB.js → WorkflowsModule-b6TLuHwJ.js} +1 -1
- package/dist/ui/assets/{index-DxRiI3tY.css → index-BiiF1cLQ.css} +1 -1
- package/dist/ui/assets/index-xBWLPOEb.js +6 -0
- package/dist/ui/index.html +2 -2
- package/package.json +3 -1
- package/scripts/deploy-prod.sh +41 -0
- package/scripts/deploy-staging.sh +23 -0
- package/dist/ui/assets/index-Bzqy5QGm.js +0 -6
package/README.md
CHANGED
|
@@ -177,6 +177,25 @@ npx @taskforcehq/taskforce open [port]
|
|
|
177
177
|
- Before changing sync logic, confirm the watched build actually rebuilt, restart the local CLI/runtime if behavior still looks stale, and then retest sync.
|
|
178
178
|
- If a sync bug seems "fixed in code but still reproducible," treat stale build output or a long-lived local runtime process as the first thing to rule out.
|
|
179
179
|
|
|
180
|
+
### Local Validation Runbooks
|
|
181
|
+
|
|
182
|
+
These checks used to exist as manual-only GitHub Actions workflows, but they now live as local runbooks to avoid burning Actions credits.
|
|
183
|
+
|
|
184
|
+
- Full surface validation:
|
|
185
|
+
- `npm ci`
|
|
186
|
+
- `npm run check:deploy-preflight`
|
|
187
|
+
- `npm run build:app`
|
|
188
|
+
- `npm run build:site`
|
|
189
|
+
- `npm run build:admin`
|
|
190
|
+
- `npm run test -- src/server/auth.test.ts src/server/routes.test.ts src/server/cors.test.ts`
|
|
191
|
+
- Auth and runtime guard:
|
|
192
|
+
- `npm ci`
|
|
193
|
+
- `npm run test:auth-runtime-guard`
|
|
194
|
+
- Sync regression guard:
|
|
195
|
+
- `npm ci`
|
|
196
|
+
- `npx vitest src/sync/workspaceSyncModel.test.ts src/sync/syncService.test.ts src/sync/cloudSyncApi.test.ts src/hooks/useWorkspaceSyncController.test.ts src/hooks/useSyncOrchestrator.retry-closure.test.tsx src/server/routes.sync.integration.test.ts src/server/routes.test.ts`
|
|
197
|
+
- optional realtime attachment check: `npm run test:e2e:realtime:attachments`
|
|
198
|
+
|
|
180
199
|
### 🧭 Initiatives
|
|
181
200
|
Initiatives provide template-based planning for complex work (for example `launch`, `migration`, `incident`) with reusable Definition of Done and Rollback checklist blocks.
|
|
182
201
|
|
|
@@ -891,10 +891,31 @@ export function StandaloneLayout(props) {
|
|
|
891
891
|
initiativeById,
|
|
892
892
|
};
|
|
893
893
|
}, [assigneeLabelByValue, hierarchyTaskById, hierarchyTasks, props.initiatives, props.workstreams, taskScope]);
|
|
894
|
-
const initiativeFilterOptions = useMemo(() => planningStructure.initiatives.map((initiative) =>
|
|
894
|
+
const initiativeFilterOptions = useMemo(() => planningStructure.initiatives.map((initiative) => {
|
|
895
|
+
const referenceLabel = formatInitiativeReference(initiative);
|
|
896
|
+
return {
|
|
897
|
+
value: initiative.id,
|
|
898
|
+
label: referenceLabel ? `${referenceLabel} - ${initiative.title}` : initiative.title,
|
|
899
|
+
selectedLabel: referenceLabel || initiative.title,
|
|
900
|
+
};
|
|
901
|
+
}), [planningStructure.initiatives]);
|
|
895
902
|
const workstreamFilterOptions = useMemo(() => [
|
|
896
|
-
...planningStructure.initiatives.flatMap((initiative) => initiative.workstreams.map((workstream) =>
|
|
897
|
-
|
|
903
|
+
...planningStructure.initiatives.flatMap((initiative) => initiative.workstreams.map((workstream) => {
|
|
904
|
+
const referenceLabel = formatWorkstreamReference(workstream);
|
|
905
|
+
return {
|
|
906
|
+
value: workstream.id,
|
|
907
|
+
label: referenceLabel ? `${referenceLabel} - ${workstream.title}` : workstream.title,
|
|
908
|
+
selectedLabel: referenceLabel || workstream.title,
|
|
909
|
+
};
|
|
910
|
+
})),
|
|
911
|
+
...planningStructure.standaloneWorkstreams.map((workstream) => {
|
|
912
|
+
const referenceLabel = formatWorkstreamReference(workstream);
|
|
913
|
+
return {
|
|
914
|
+
value: workstream.id,
|
|
915
|
+
label: referenceLabel ? `${referenceLabel} - ${workstream.title}` : workstream.title,
|
|
916
|
+
selectedLabel: referenceLabel || workstream.title,
|
|
917
|
+
};
|
|
918
|
+
}),
|
|
898
919
|
], [planningStructure.initiatives, planningStructure.standaloneWorkstreams]);
|
|
899
920
|
const selectedPlanningTaskIds = useMemo(() => {
|
|
900
921
|
if (selectedPlanningWorkstreamId) {
|
|
@@ -45,12 +45,14 @@ export interface FilterToolbarProps {
|
|
|
45
45
|
initiativeFilterOptions?: Array<{
|
|
46
46
|
value: string;
|
|
47
47
|
label: string;
|
|
48
|
+
selectedLabel?: string;
|
|
48
49
|
}>;
|
|
49
50
|
selectedInitiativeId?: string;
|
|
50
51
|
setSelectedInitiativeId?: (value: string) => void;
|
|
51
52
|
workstreamFilterOptions?: Array<{
|
|
52
53
|
value: string;
|
|
53
54
|
label: string;
|
|
55
|
+
selectedLabel?: string;
|
|
54
56
|
}>;
|
|
55
57
|
selectedWorkstreamId?: string;
|
|
56
58
|
setSelectedWorkstreamId?: (value: string) => void;
|
|
@@ -27,7 +27,7 @@ export function FilterToolbarSingleSelect({ label, options, selected, onChange,
|
|
|
27
27
|
const [isOpen, setIsOpen] = React.useState(false);
|
|
28
28
|
const containerRef = React.useRef(null);
|
|
29
29
|
const selectedOption = options.find((option) => option.value === selected);
|
|
30
|
-
const displayText = selectedOption?.label || allLabel;
|
|
30
|
+
const displayText = selectedOption?.selectedLabel || selectedOption?.label || allLabel;
|
|
31
31
|
React.useEffect(() => {
|
|
32
32
|
const handleClickOutside = (event) => {
|
|
33
33
|
if (containerRef.current && !containerRef.current.contains(event.target)) {
|
|
@@ -64,7 +64,7 @@ function PlanningRow({ rowId, rowType, referenceLabel, title, meta, active, deta
|
|
|
64
64
|
? { transform: CSS.Translate.toString(transform) }
|
|
65
65
|
: undefined;
|
|
66
66
|
const showDropReady = canDropTask || canDropWorkstream;
|
|
67
|
-
return (_jsxs("div", { ref: setDropRef, style: dragStyle, className: `${panelStyles.treeRow} ${detailOpen ? panelStyles.treeRowDetailOpen : ''} ${showDropReady ? panelStyles.treeRowDropReady : ''} ${isOver ? panelStyles.treeRowDropActive : ''} ${isDragging ? panelStyles.treeRowDragging : ''} ${showDropReady ? panelStyles.treeRowDropMode : ''}`, children: [canExpand ? (_jsx("button", { type: "button", className: panelStyles.treeChevron, onClick: onToggleExpand, title: expanded ? 'Collapse workstreams' : 'Expand workstreams', children: expanded ? _jsx(ChevronsDown, { size: 15 }) : _jsx(ChevronsRight, { size: 15 }) })) : (_jsx("span", { className: panelStyles.treeChevron, "aria-hidden": "true", children: _jsx(ChevronRight, { size: 14 }) })), _jsxs("div", { className: panelStyles.rowContent, children: [referenceLabel ? (_jsx(PlanningReferenceBadge, { label: referenceLabel, entityType: rowType })) :
|
|
67
|
+
return (_jsxs("div", { ref: setDropRef, style: dragStyle, className: `${panelStyles.treeRow} ${detailOpen ? panelStyles.treeRowDetailOpen : ''} ${showDropReady ? panelStyles.treeRowDropReady : ''} ${isOver ? panelStyles.treeRowDropActive : ''} ${isDragging ? panelStyles.treeRowDragging : ''} ${showDropReady ? panelStyles.treeRowDropMode : ''}`, children: [canExpand ? (_jsx("button", { type: "button", className: panelStyles.treeChevron, onClick: onToggleExpand, title: expanded ? 'Collapse workstreams' : 'Expand workstreams', children: expanded ? _jsx(ChevronsDown, { size: 15 }) : _jsx(ChevronsRight, { size: 15 }) })) : (_jsx("span", { className: panelStyles.treeChevron, "aria-hidden": "true", children: _jsx(ChevronRight, { size: 14 }) })), _jsxs("div", { className: panelStyles.rowContent, children: [_jsxs("div", { className: panelStyles.rowTopRow, children: [referenceLabel ? (_jsx(PlanningReferenceBadge, { label: referenceLabel, entityType: rowType })) : _jsx("span", { "aria-hidden": "true" }), _jsx("button", { type: "button", className: `${panelStyles.detailsButton} ${active ? panelStyles.detailsButtonActive : ''}`, onClick: onToggleScope, title: active ? 'Clear board scope' : 'Scope board to this item', children: _jsx(Filter, { size: 14 }) })] }), _jsxs("button", { type: "button", ref: draggable ? setDragRef : undefined, className: panelStyles.rowButton, onClick: onToggleDetails, ...(draggable ? attributes : {}), ...(draggable ? listeners : {}), children: [_jsx("span", { className: panelStyles.rowTitle, children: title }), _jsx("span", { className: panelStyles.rowMeta, children: meta })] })] })] }));
|
|
68
68
|
}
|
|
69
69
|
function CollapsedPane({ label, onExpand, }) {
|
|
70
70
|
return (_jsx("div", { className: `${panelStyles.paneShell} ${panelStyles.collapsedPaneShell}`.trim(), children: _jsxs("div", { className: panelStyles.collapsedPaneHeader, children: [_jsx("button", { type: "button", className: "tf-control-icon", onClick: onExpand, title: `Expand ${label}`, children: _jsx(ChevronRight, { size: 16 }) }), _jsx("span", { className: panelStyles.collapsedPaneLabel, children: label })] }) }));
|
|
@@ -45,6 +45,16 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
45
45
|
const getTaskRevisionKey = useCallback((task) => {
|
|
46
46
|
return `${task.updatedAt || task.createdAt || ''}|${task.status}|${task.priority}|${task.title}`;
|
|
47
47
|
}, []);
|
|
48
|
+
const isExpectedAbortFailure = useCallback((error, signal) => {
|
|
49
|
+
if (!signal.aborted)
|
|
50
|
+
return false;
|
|
51
|
+
if (error === signal.reason)
|
|
52
|
+
return true;
|
|
53
|
+
if (error instanceof DOMException) {
|
|
54
|
+
return error.name === 'AbortError';
|
|
55
|
+
}
|
|
56
|
+
return String(error?.name || '').toLowerCase() === 'aborterror';
|
|
57
|
+
}, []);
|
|
48
58
|
// === SECTION: Recently Changed ===
|
|
49
59
|
const [recentlyChangedTaskIds, setRecentlyChangedTaskIds] = useState([]);
|
|
50
60
|
const highlightTimersRef = useRef(new Map());
|
|
@@ -137,10 +147,7 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
137
147
|
}
|
|
138
148
|
}
|
|
139
149
|
catch (err) {
|
|
140
|
-
|
|
141
|
-
? err.name === 'AbortError'
|
|
142
|
-
: String(err?.name || '').toLowerCase() === 'aborterror';
|
|
143
|
-
if (isAbortError)
|
|
150
|
+
if (isExpectedAbortFailure(err, abortController.signal))
|
|
144
151
|
return;
|
|
145
152
|
console.error('[Taskforce] Failed to fetch tasks:', err);
|
|
146
153
|
}
|
|
@@ -186,10 +193,7 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
186
193
|
}
|
|
187
194
|
}
|
|
188
195
|
catch (err) {
|
|
189
|
-
|
|
190
|
-
? err.name === 'AbortError'
|
|
191
|
-
: String(err?.name || '').toLowerCase() === 'aborterror';
|
|
192
|
-
if (isAbortError)
|
|
196
|
+
if (isExpectedAbortFailure(err, abortController.signal))
|
|
193
197
|
return;
|
|
194
198
|
console.error('[Taskforce] Failed to fetch archive:', err);
|
|
195
199
|
}
|
|
@@ -198,7 +202,7 @@ export function useTaskData({ tasks, archivedTasks, setTasks, setArchivedTasks,
|
|
|
198
202
|
archiveAbortRef.current = null;
|
|
199
203
|
}
|
|
200
204
|
}
|
|
201
|
-
}, [getCurrentWorkspaceId, setArchivedTasks, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls]);
|
|
205
|
+
}, [getCurrentWorkspaceId, isExpectedAbortFailure, setArchivedTasks, shouldDeferProtectedApiCalls, shouldBlockProtectedApiCalls]);
|
|
202
206
|
const refreshTaskCollections = useCallback(async (options) => {
|
|
203
207
|
const isSilent = options?.isSilent !== false;
|
|
204
208
|
const ignoreAuthGuard = options?.ignoreAuthGuard === true;
|
|
@@ -23,6 +23,27 @@ function makeAbortableRequest(payload) {
|
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
+
function makeAbortableRequestRejectingWithReason(payload) {
|
|
27
|
+
let rejectRequest = null;
|
|
28
|
+
let resolveRequest = null;
|
|
29
|
+
const promise = new Promise((resolve, reject) => {
|
|
30
|
+
resolveRequest = resolve;
|
|
31
|
+
rejectRequest = reject;
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
promise,
|
|
35
|
+
attach(signal) {
|
|
36
|
+
signal?.addEventListener('abort', () => {
|
|
37
|
+
rejectRequest?.(signal.reason);
|
|
38
|
+
}, { once: true });
|
|
39
|
+
queueMicrotask(() => {
|
|
40
|
+
if (!signal?.aborted) {
|
|
41
|
+
resolveRequest?.(makeAbortableFetchResponse(payload));
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
26
47
|
describe('useTaskData', () => {
|
|
27
48
|
beforeEach(() => {
|
|
28
49
|
vi.stubGlobal('fetch', vi.fn());
|
|
@@ -116,4 +137,44 @@ describe('useTaskData', () => {
|
|
|
116
137
|
expect(requestSignals[0]?.aborted).toBe(true);
|
|
117
138
|
expect(requestSignals[1]?.aborted).toBe(false);
|
|
118
139
|
});
|
|
140
|
+
it('does not log an error when an archive request is aborted with a string reason', async () => {
|
|
141
|
+
const firstRequest = makeAbortableRequestRejectingWithReason({ archived: [] });
|
|
142
|
+
const secondRequest = makeAbortableRequestRejectingWithReason({ archived: [] });
|
|
143
|
+
const fetchMock = vi.mocked(fetch);
|
|
144
|
+
const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => { });
|
|
145
|
+
let requestCount = 0;
|
|
146
|
+
fetchMock.mockImplementation((_input, init) => {
|
|
147
|
+
const signal = init?.signal;
|
|
148
|
+
requestCount += 1;
|
|
149
|
+
if (requestCount === 1) {
|
|
150
|
+
firstRequest.attach(signal);
|
|
151
|
+
return firstRequest.promise;
|
|
152
|
+
}
|
|
153
|
+
secondRequest.attach(signal);
|
|
154
|
+
return secondRequest.promise;
|
|
155
|
+
});
|
|
156
|
+
const { result } = renderHook(() => useTaskData({
|
|
157
|
+
tasks: [],
|
|
158
|
+
archivedTasks: [],
|
|
159
|
+
setTasks: vi.fn(),
|
|
160
|
+
setArchivedTasks: vi.fn(),
|
|
161
|
+
setLoadingTasks: vi.fn(),
|
|
162
|
+
getCurrentWorkspaceId: () => 'workspace-1',
|
|
163
|
+
workspaceResetKey: 'workspace-1',
|
|
164
|
+
shouldDeferProtectedApiCalls: false,
|
|
165
|
+
shouldBlockProtectedApiCalls: false,
|
|
166
|
+
handleUnauthorized: vi.fn(),
|
|
167
|
+
authRequiredForApi: false
|
|
168
|
+
}));
|
|
169
|
+
let firstPromise;
|
|
170
|
+
let secondPromise;
|
|
171
|
+
await act(async () => {
|
|
172
|
+
firstPromise = result.current.fetchArchive(true);
|
|
173
|
+
secondPromise = result.current.fetchArchive(true);
|
|
174
|
+
await secondPromise;
|
|
175
|
+
await firstPromise;
|
|
176
|
+
});
|
|
177
|
+
expect(consoleErrorSpy).not.toHaveBeenCalledWith('[Taskforce] Failed to fetch archive:', 'superseded');
|
|
178
|
+
expect(consoleErrorSpy).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
119
180
|
});
|
package/dist/mcp/runtime.js
CHANGED
|
@@ -2974,15 +2974,15 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
2974
2974
|
{
|
|
2975
2975
|
name: "save_task_attachment",
|
|
2976
2976
|
description: describeTool(runtimeMode === 'cloud'
|
|
2977
|
-
? "Save generated content into a task attachment and link it to the task. Use this when an agent creates notes, reports, or artifacts that should live with the task instead of remaining only in chat. When overwrite=true, this replaces an existing canonical attachment with the same logical filename in place. External URL attachments are never overwrite targets; if no matching canonical attachment exists, a new attachment is created."
|
|
2977
|
+
? "Save generated content into a task attachment and link it to the task. Use this when an agent creates notes, reports, or artifacts that should live with the task instead of remaining only in chat. For cloud MCP, AI-authored markdown/text should usually be passed inline via content rather than sourcePath. When overwrite=true, this replaces an existing canonical attachment with the same logical filename in place. External URL attachments are never overwrite targets; if no matching canonical attachment exists, a new attachment is created."
|
|
2978
2978
|
: "Save generated content or copy a file into a task-specific attachment and link it to the task. Use this when an agent creates notes, reports, or artifacts that should live with the task instead of remaining only in chat. When overwrite=true, this replaces an existing canonical attachment with the same logical filename in place. External URL attachments are never overwrite targets; if no matching canonical attachment exists, a new attachment is created."),
|
|
2979
2979
|
inputSchema: {
|
|
2980
2980
|
type: "object",
|
|
2981
2981
|
properties: {
|
|
2982
2982
|
id: { type: "string", description: "Task ID" },
|
|
2983
2983
|
filename: { type: "string", description: "Target filename for the task attachment." },
|
|
2984
|
-
content: { type: "string", description: runtimeMode === 'cloud' ? "Text/markdown file content to save." : "Text/markdown file content to save. Provide either content or sourcePath." },
|
|
2985
|
-
sourcePath: { type: "string", description: runtimeMode === 'cloud' ? "Project-relative or absolute source file path to copy (local runtime only)." : "Project-relative or absolute source file path to copy. Provide either sourcePath or content." },
|
|
2984
|
+
content: { type: "string", description: runtimeMode === 'cloud' ? "Text/markdown file content to save. For cloud MCP, use this for AI-authored markdown/text attachments." : "Text/markdown file content to save. Provide either content or sourcePath." },
|
|
2985
|
+
sourcePath: { type: "string", description: runtimeMode === 'cloud' ? "Project-relative or absolute source file path to copy (local runtime only today). For cloud MCP, prefer inline content for AI-authored markdown/text." : "Project-relative or absolute source file path to copy. Provide either sourcePath or content." },
|
|
2986
2986
|
caption: { type: "string", description: "Optional label shown in task context" },
|
|
2987
2987
|
overwrite: { type: "boolean", description: "Overwrite the destination file when it already exists (default: false)." }
|
|
2988
2988
|
},
|
|
@@ -5199,7 +5199,7 @@ export function createTaskforceMcpServer(options = {}) {
|
|
|
5199
5199
|
throw new Error("Provide exactly one of: content or sourcePath");
|
|
5200
5200
|
}
|
|
5201
5201
|
if (runtimeMode === 'cloud' && hasSourcePath) {
|
|
5202
|
-
throw new Error("Cloud MCP
|
|
5202
|
+
throw new Error("Cloud MCP does not support sourcePath for save_task_attachment yet. For AI-authored markdown or text, resend the attachment using inline content instead.");
|
|
5203
5203
|
}
|
|
5204
5204
|
const now = new Date().toISOString();
|
|
5205
5205
|
const safeCaption = typeof caption === 'string' && caption.trim().length > 0
|
package/dist/mcp/runtime.test.js
CHANGED
|
@@ -423,6 +423,11 @@ describe('Taskforce MCP tool descriptions', () => {
|
|
|
423
423
|
expect(linkTaskContext.description).toContain('external http(s) URL');
|
|
424
424
|
expect(saveTaskAttachment.description).toContain('generated content');
|
|
425
425
|
expect(saveTaskAttachment.description).toContain('overwrite=true');
|
|
426
|
+
expect(saveTaskAttachment.description).toContain('AI-authored markdown/text should usually be passed inline via content');
|
|
427
|
+
expect(saveTaskAttachment.inputSchema).toBeDefined();
|
|
428
|
+
const saveTaskAttachmentSchema = saveTaskAttachment.inputSchema;
|
|
429
|
+
expect(saveTaskAttachmentSchema.properties.content.description).toContain('use this for AI-authored markdown/text attachments');
|
|
430
|
+
expect(saveTaskAttachmentSchema.properties.sourcePath.description).toContain('local runtime only today');
|
|
426
431
|
expect(resolveProfile.description).toContain('AI collaborator profile');
|
|
427
432
|
expect(resolveProfile.description).toContain('Update semantics');
|
|
428
433
|
expect(resolveProfile.description).toContain('empty strings are ignored rather than clearing');
|
|
@@ -1732,7 +1737,8 @@ describe('Taskforce MCP tool descriptions', () => {
|
|
|
1732
1737
|
filename: 'copy.txt',
|
|
1733
1738
|
sourcePath: 'docs/source.txt'
|
|
1734
1739
|
});
|
|
1735
|
-
expect(result.content?.[0]?.text).toContain('
|
|
1740
|
+
expect(result.content?.[0]?.text).toContain('does not support sourcePath for save_task_attachment yet');
|
|
1741
|
+
expect(result.content?.[0]?.text).toContain('resend the attachment using inline content instead');
|
|
1736
1742
|
}
|
|
1737
1743
|
finally {
|
|
1738
1744
|
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as n,j as e}from"./vendor-react-CKJs5o3c.js";import{s as T,u as C,v as w,w as S,t as s,x as M}from"./index-
|
|
1
|
+
import{r as n,j as e}from"./vendor-react-CKJs5o3c.js";import{s as T,u as C,v as w,w as S,t as s,x as M}from"./index-xBWLPOEb.js";import{h as L,B as A,k as I,a8 as H,aG as B,aH as D,b as R}from"./vendor-icons-QZyhEwgT.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function F({workspaceId:h}){const[x,k]=n.useState([]),[j,y]=n.useState(!1),[c,P]=n.useState(null),[p,m]=n.useState(null),[d,g]=n.useState(null),N=n.useCallback(async()=>{if(h){y(!0);try{const a=await(await fetch(`/api/taskforce/workspace/assignee-options?workspaceId=${encodeURIComponent(h)}`,{credentials:"include"})).json().catch(()=>({})),t=Array.isArray(a?.assignees)?a.assignees.filter(i=>i.kind==="agent").map(i=>({id:String(i.value||""),name:String(i.label||i.value||"Unknown Agent"),username:String(i.username||i.value||""),icon:String(i.icon||"Bot"),color:String(i.color||"#6B7280"),kind:String(i.kind||"agent"),description:typeof i.description=="string"?i.description:null,role:typeof i.role=="string"?i.role:null,provider:typeof i.provider=="string"?i.provider:null,model:typeof i.model=="string"?i.model:null,surfaceType:T(i.surfaceType),createdAt:String(i.createdAt||""),updatedAt:String(i.updatedAt||i.createdAt||""),lastActiveAt:typeof i.lastActiveAt=="string"?i.lastActiveAt:null})):[];k(t)}finally{y(!1)}}},[h]);n.useEffect(()=>{N()},[N]);const f=n.useMemo(()=>{const r=new Map;for(const a of x){const t=C(a.surfaceType);r.has(t)||r.set(t,new Map);const i=r.get(t),v=a.name.toLowerCase();i.has(v)||i.set(v,[]),i.get(v).push(a)}return w.map(a=>({section:a,label:S(a),groups:Array.from(r.get(a)?.entries()||[]).map(([t,i])=>({groupId:`${a}:${t}`,section:a,sectionLabel:S(a),profiles:i,primaryProfile:i[0]}))})).filter(a=>a.groups.length>0)},[x]),o=n.useMemo(()=>f.flatMap(r=>r.groups),[f]),l=n.useMemo(()=>o.find(r=>r.groupId===d)||o[0]||null,[o,d]);n.useEffect(()=>{if(!o.length){d!==null&&g(null);return}(!d||!o.some(r=>r.groupId===d))&&g(o[0].groupId)},[o,d]);const b=async(r,a)=>{m(null);try{const t=await fetch("/api/taskforce/workspace/ai-profiles/merge",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"},body:JSON.stringify({keepId:r,mergeId:a})}),i=await t.json().catch(()=>({}));if(!t.ok){m({type:"error",message:String(i?.error||"Failed to merge AI profiles.")});return}P(null),m({type:"success",message:"AI profiles merged."}),await N()}catch{m({type:"error",message:"Failed to merge AI profiles."})}},u=r=>{const a=String(r||"").trim();if(!a)return"Unknown";const t=Date.parse(a);return Number.isFinite(t)?new Date(t).toLocaleString(void 0,{dateStyle:"medium",timeStyle:"short"}):a};return e.jsx("div",{className:`${s.standalonePage} ${s.standaloneContent}`,style:{overflow:"auto"},children:e.jsx("div",{className:s.settingsContent,style:{maxWidth:960,margin:"0 auto",width:"100%"},children:e.jsx("div",{className:s.settingGroup,children:e.jsxs("div",{children:[e.jsx("h4",{className:s.settingSubTitle,children:"Registered AI Profiles"}),e.jsx("p",{className:`${s.settingsHint} ${s.marginBottom12}`,children:"AI agents register profiles when connecting via MCP. Merge duplicates created when a token was lost."}),j&&e.jsxs("div",{className:s.settingsHint,children:[e.jsx(L,{size:13,className:s.spinner})," Loading profiles…"]}),!j&&x.length===0&&e.jsx("div",{className:s.settingsHint,children:"No AI profiles registered yet."}),!j&&f.length>0&&e.jsxs("div",{className:s.aiProfilesExplorer,children:[e.jsx("div",{className:s.aiProfilesListPane,children:e.jsx("div",{className:s.aiProfilesList,children:f.map(r=>e.jsxs("div",{className:s.aiProfilesSection,children:[e.jsx("div",{className:s.aiProfilesSectionHeader,children:r.label}),r.groups.map(a=>{const t=l?.groupId===a.groupId,i=a.primaryProfile;return e.jsxs("button",{type:"button",className:`${s.aiProfileGroup} ${a.profiles.length>1?s.aiProfileGroupDuplicate:""} ${t?s.aiProfileGroupSelected:""}`,onClick:()=>g(a.groupId),"aria-pressed":t,children:[e.jsxs("div",{className:s.aiProfileGroupHeader,children:[e.jsx(A,{size:13,style:{color:i.color}}),e.jsx("span",{className:s.aiProfileName,children:i.name}),a.profiles.length>1&&e.jsxs("span",{className:s.aiProfileDuplicateBadge,children:[e.jsx(I,{size:11})," ",a.profiles.length," duplicates"]})]}),e.jsxs("div",{className:s.settingsHint,style:{marginTop:6},children:["@",i.username]}),(i.surfaceType||i.role||i.provider||i.model)&&e.jsxs("div",{className:s.aiProfileMetaRow,children:[i.surfaceType&&e.jsx("span",{className:s.aiProfileSurfaceBadge,children:M(i.surfaceType)}),i.role&&e.jsx("span",{children:i.role}),i.provider&&e.jsx("span",{children:i.provider}),i.model&&e.jsx("span",{children:i.model})]}),i.description&&e.jsx("div",{className:s.settingsHint,style:{marginTop:4},children:i.description})]},a.groupId)})]},r.section))})}),l&&e.jsx("div",{className:s.aiProfileDetailPane,children:e.jsxs("div",{className:s.aiProfileDetailCard,children:[e.jsxs("div",{className:s.aiProfileDetailHero,children:[e.jsx("div",{className:s.aiProfileDetailIcon,style:{color:l.primaryProfile.color},children:e.jsx(A,{size:18})}),e.jsxs("div",{className:s.aiProfileDetailHeading,children:[e.jsx("div",{className:s.aiProfileDetailTitleRow,children:e.jsx("h5",{className:s.aiProfileDetailTitle,children:l.primaryProfile.name})}),e.jsxs("div",{className:s.aiProfileDetailMetaRow,children:[e.jsxs("span",{className:s.aiProfileDetailHandle,children:["@",l.primaryProfile.username]}),e.jsx("span",{className:s.aiProfileDetailSectionBadge,children:l.sectionLabel}),l.profiles.length>1&&e.jsxs("span",{className:s.aiProfileDetailLinkedCount,children:[l.profiles.length," linked"]})]})]})]}),l.primaryProfile.description&&e.jsx("div",{className:s.aiProfileDetailDescription,children:l.primaryProfile.description}),e.jsxs("div",{className:s.aiProfileDetailGrid,children:[e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(H,{size:12})," Role"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:l.primaryProfile.role||"Not set"})]}),e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(B,{size:12})," Provider"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:[l.primaryProfile.provider,l.primaryProfile.model].filter(Boolean).join(" · ")||"Not set"})]}),e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(D,{size:12})," Last active"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:u(l.primaryProfile.lastActiveAt)})]}),e.jsxs("div",{className:s.aiProfileDetailStat,children:[e.jsxs("span",{className:s.aiProfileDetailStatLabel,children:[e.jsx(D,{size:12})," Updated"]}),e.jsx("span",{className:s.aiProfileDetailStatValue,children:u(l.primaryProfile.updatedAt)})]})]}),e.jsxs("div",{className:s.aiProfileInstanceSection,children:[e.jsxs("div",{className:s.aiProfileInstanceSectionHeader,children:[e.jsx("span",{children:"Profile instances"}),e.jsx("span",{className:s.settingsHint,children:"Choose a keeper here if duplicates need to be merged."})]}),e.jsx("div",{className:s.aiProfileInstanceList,children:l.profiles.map(r=>e.jsxs("div",{className:s.aiProfileInstanceCard,children:[e.jsxs("div",{className:s.aiProfileInstanceTopRow,children:[e.jsxs("div",{children:[e.jsxs("div",{className:s.aiProfileInstanceName,children:["@",r.username]}),e.jsx("div",{className:s.aiProfileIdChip,children:r.id})]}),l.profiles.length>1&&c?.keepId===r.id&&e.jsx("span",{className:s.aiProfileKeepBadge,children:"Keeping"})]}),e.jsxs("div",{className:s.aiProfileInstanceMeta,children:[e.jsxs("span",{children:["Created ",u(r.createdAt)]}),e.jsxs("span",{children:["Updated ",u(r.updatedAt)]})]}),l.profiles.length>1&&c?.keepId!==r.id&&e.jsx("button",{className:s.secondaryHeaderBtn,title:"Keep this profile, merge others into it",onClick:()=>{const a=l.profiles.find(t=>t.id!==r.id)?.id;a&&P({keepId:r.id,mergeId:a})},children:"Keep this"})]},r.id))}),c&&l.profiles.some(r=>r.id===c.keepId)&&e.jsxs("div",{className:s.aiProfileMergeActions,children:[e.jsx("button",{className:s.dangerBtn,onClick:()=>{b(c.keepId,c.mergeId)},children:"Merge duplicates"}),e.jsx("button",{className:s.secondaryHeaderBtn,onClick:()=>P(null),children:"Cancel"})]})]})]})})]}),p&&e.jsxs("div",{className:`${p.type==="success"?s.successMessage:s.errorMessage} ${s.marginTop12}`,children:[p.type==="success"?e.jsx(R,{size:14}):e.jsx(I,{size:14}),p.message]})]})})})})}export{F as AgentsModule};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{j as t,r as a,R as qt}from"./vendor-react-CKJs5o3c.js";import{R as xs,f as Ce,e as Ct,g as vs,t as Bn,M as Jt}from"./index-Bzqy5QGm.js";import{q as _s,m as js,c as Zt,T as Tn,s as ks,a1 as ws,h as An,ai as Cs,R as Is,aj as Ss,ak as Ns,al as $s,am as tn,C as nn,an as Rs,ao as Dn,ap as zn,aq as Un,ar as Gn,g as Ps,f as Bs,b as sn}from"./vendor-icons-QZyhEwgT.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Ts({copied:o,disabled:l=!1,label:m,onClick:g,title:w="Copy image reference",ariaLabel:y,className:M=""}){return t.jsx(xs,{copied:o,disabled:l,label:"",onClick:g,title:w,ariaLabel:y,className:M,children:m})}const As="_shell_sf7jc_1",Es="_panel_sf7jc_16",Ms="_sessionPanel_sf7jc_22",Ls="_detailPanel_sf7jc_23",Fs="_sessionContextBar_sf7jc_29",Hs="_sessionContextLeft_sf7jc_40",Os="_sessionContextRight_sf7jc_41",Ds="_sessionContextLabel_sf7jc_56",zs="_sessionContextSpacer_sf7jc_62",Us="_canvasPanel_sf7jc_67",Gs="_canvasWorkspace_sf7jc_74",Ys="_canvasMain_sf7jc_81",Ks="_panelHeader_sf7jc_88",Ws="_panelHeaderText_sf7jc_97",Xs="_panelTitle_sf7jc_101",Vs="_canvasHeading_sf7jc_105",qs="_sessionActions_sf7jc_109",Js="_sessionList_sf7jc_117",Zs="_annotationList_sf7jc_118",Qs="_markerHelpModalBody_sf7jc_133",ea="_openImageModalBody_sf7jc_139",ta="_openImageField_sf7jc_145",na="_openImageActions_sf7jc_149",sa="_markerHelpItem_sf7jc_155",aa="_markerHelpHeader_sf7jc_163",oa="_markerHelpExample_sf7jc_175",ra="_sessionCard_sf7jc_179",ia="_annotationCard_sf7jc_180",la="_sessionEmptyState_sf7jc_197",ca="_sessionCardButton_sf7jc_204",da="_annotationCardButton_sf7jc_214",ua="_sessionCardBody_sf7jc_224",fa="_sessionCardActive_sf7jc_230",ma="_annotationCardActive_sf7jc_231",pa="_annotationMeta_sf7jc_241",ha="_annotationInstructionPreview_sf7jc_248",ba="_annotationPreviewFooter_sf7jc_256",ga="_annotationInstructionEditor_sf7jc_263",ya="_annotationTypeField_sf7jc_269",xa="_annotationInstructionButton_sf7jc_275",va="_annotationInstructionField_sf7jc_284",_a="_sessionMeta_sf7jc_288",ja="_sessionTitle_sf7jc_295",ka="_annotationTitle_sf7jc_296",wa="_sessionTimestamp_sf7jc_302",Ca="_annotationKind_sf7jc_303",Ia="_annotationInstructionTypeIcon_sf7jc_307",Sa="_sessionInstructionPreview_sf7jc_313",Na="_sessionInstructionEditor_sf7jc_321",$a="_sessionCardFooter_sf7jc_327",Ra="_toolRail_sf7jc_334",Pa="_canvasToolRail_sf7jc_343",Ba="_toolbarCluster_sf7jc_364",Ta="_toolbarViewportCluster_sf7jc_371",Aa="_toolbarSeparator_sf7jc_375",Ea="_toolBtn_sf7jc_381",Ma="_toolRailButton_sf7jc_385",La="_toolbarButton_sf7jc_395",Fa="_toolBtnActive_sf7jc_400",Ha="_toolbarActions_sf7jc_407",Oa="_toolbarSelectionActions_sf7jc_416",Da="_toolbarColorPicker_sf7jc_424",za="_colorPickerButton_sf7jc_428",Ua="_colorPickerSwatch_sf7jc_433",Ga="_colorPickerPopover_sf7jc_441",Ya="_colorOption_sf7jc_456",Ka="_colorOptionActive_sf7jc_466",Wa="_toolbarUtilities_sf7jc_473",Xa="_toolbarGeometryFields_sf7jc_481",Va="_toolbarGeometryField_sf7jc_481",qa="_toolbarGeometryLabel_sf7jc_494",Ja="_toolbarGeometryInput_sf7jc_500",Za="_iconButton_sf7jc_505",Qa="_ghostBtn_sf7jc_510",eo="_payloadBtn_sf7jc_511",to="_backToTaskBtn_sf7jc_512",no="_canvasScroller_sf7jc_532",so="_canvasFrame_sf7jc_548",ao="_canvasMedia_sf7jc_555",oo="_canvasStatusOverlay_sf7jc_563",ro="_canvasStatusCard_sf7jc_575",io="_canvasImage_sf7jc_588",lo="_overlay_sf7jc_595",co="_overlaySelect_sf7jc_601",uo="_overlayPan_sf7jc_605",fo="_overlaySvg_sf7jc_609",mo="_overlayHitLayer_sf7jc_618",po="_arrowHitArea_sf7jc_627",ho="_canvasHandleHit_sf7jc_634",bo="_canvasResizeHandleHit_sf7jc_641",go="_canvasHandleVisible_sf7jc_645",yo="_pin_sf7jc_662",xo="_note_sf7jc_663",vo="_annotationNumberBadge_sf7jc_680",_o="_box_sf7jc_705",jo="_boxNumberBadge_sf7jc_714",ko="_arrowNumberBadge_sf7jc_720",wo="_boxSurface_sf7jc_724",Co="_selected_sf7jc_737",Io="_textInput_sf7jc_754",So="_textArea_sf7jc_755",No="_select_sf7jc_737",$o="_sessionTitleInput_sf7jc_762",Ro="_sessionInstructionField_sf7jc_767",Po="_detailEmpty_sf7jc_776",Bo="_emptyState_sf7jc_777",To="_payloadModalBody_sf7jc_789",Ao="_payloadModalToolbar_sf7jc_796",Eo="_payloadViewToggle_sf7jc_803",Mo="_payloadModalActions_sf7jc_804",Lo="_payloadModalPreview_sf7jc_811",Fo="_statusBar_sf7jc_826",Ho="_annotationSummary_sf7jc_838",s={shell:As,panel:Es,sessionPanel:Ms,detailPanel:Ls,sessionContextBar:Fs,sessionContextLeft:Hs,sessionContextRight:Os,sessionContextLabel:Ds,sessionContextSpacer:zs,canvasPanel:Us,canvasWorkspace:Gs,canvasMain:Ys,panelHeader:Ks,panelHeaderText:Ws,panelTitle:Xs,canvasHeading:Vs,sessionActions:qs,sessionList:Js,annotationList:Zs,markerHelpModalBody:Qs,openImageModalBody:ea,openImageField:ta,openImageActions:na,markerHelpItem:sa,markerHelpHeader:aa,markerHelpExample:oa,sessionCard:ra,annotationCard:ia,sessionEmptyState:la,sessionCardButton:ca,annotationCardButton:da,sessionCardBody:ua,sessionCardActive:fa,annotationCardActive:ma,annotationMeta:pa,annotationInstructionPreview:ha,annotationPreviewFooter:ba,annotationInstructionEditor:ga,annotationTypeField:ya,annotationInstructionButton:xa,annotationInstructionField:va,sessionMeta:_a,sessionTitle:ja,annotationTitle:ka,sessionTimestamp:wa,annotationKind:Ca,annotationInstructionTypeIcon:Ia,sessionInstructionPreview:Sa,sessionInstructionEditor:Na,sessionCardFooter:$a,toolRail:Ra,canvasToolRail:Pa,toolbarCluster:Ba,toolbarViewportCluster:Ta,toolbarSeparator:Aa,toolBtn:Ea,toolRailButton:Ma,toolbarButton:La,toolBtnActive:Fa,toolbarActions:Ha,toolbarSelectionActions:Oa,toolbarColorPicker:Da,colorPickerButton:za,colorPickerSwatch:Ua,colorPickerPopover:Ga,colorOption:Ya,colorOptionActive:Ka,toolbarUtilities:Wa,toolbarGeometryFields:Xa,toolbarGeometryField:Va,toolbarGeometryLabel:qa,toolbarGeometryInput:Ja,iconButton:Za,ghostBtn:Qa,payloadBtn:eo,backToTaskBtn:to,canvasScroller:no,canvasFrame:so,canvasMedia:ao,canvasStatusOverlay:oo,canvasStatusCard:ro,canvasImage:io,overlay:lo,overlaySelect:co,overlayPan:uo,overlaySvg:fo,overlayHitLayer:mo,arrowHitArea:po,canvasHandleHit:ho,canvasResizeHandleHit:bo,canvasHandleVisible:go,pin:yo,note:xo,annotationNumberBadge:vo,box:_o,boxNumberBadge:jo,arrowNumberBadge:ko,boxSurface:wo,selected:Co,textInput:Io,textArea:So,select:No,sessionTitleInput:$o,sessionInstructionField:Ro,detailEmpty:Po,emptyState:Bo,payloadModalBody:To,payloadModalToolbar:Ao,payloadViewToggle:Eo,payloadModalActions:Mo,payloadModalPreview:Lo,statusBar:Fo,annotationSummary:Ho},En=[{value:"review",label:"Review"},{value:"change",label:"Change"},{value:"question",label:"Question"}],Mn=[{value:"select",label:"Select",icon:Rs},{value:"pin",label:"Pin",icon:Dn},{value:"box",label:"Box",icon:zn},{value:"arrow",label:"Arrow",icon:Un},{value:"text-note",label:"Note",icon:Gn}],Oo={pin:Dn,box:zn,arrow:Un,"text-note":Gn},Do={pin:"Pin",box:"Box",arrow:"Arrow","text-note":"Note"},zo={review:tn,change:sn,question:nn,issue:nn,idea:tn},It={select:{short:"Select and edit existing markers.",detail:"Use Select to click, drag, reorder, resize, and update markers that are already on the image.",example:"Example: move an existing marker after the screenshot changes."},pin:{short:"Mark a precise spot.",detail:"Use Pin when feedback points to one exact location instead of a broader area.",example:'Example: "This icon is misaligned by 2px."'},box:{short:"Mark an area or component.",detail:"Use Box when the feedback applies to a whole region, card, panel, or bounded UI block.",example:'Example: "This whole card needs tighter padding and a stronger border."'},arrow:{short:"Show direction or relationship.",detail:"Use Arrow when you need to show movement, attachment, flow, or source-to-target intent.",example:'Example: "This tooltip should anchor to this button, not the panel."'},"text-note":{short:"Add a comment-style point marker.",detail:"Use Note when you want a point marker that reads more like a comment or open question.",example:'Example: "Ask design whether this badge should stay."'}},it={question:"#0f766e",change:"#2563eb",issue:"#dc2626",idea:"#d97706",review:"#7c3aed"},Uo=["#7c3aed","#2563eb","#0f766e","#dc2626","#d97706","#111827"],re="review";function ze(o){const l=String(o.displayName||"").trim();return l?`${l} review`:"Annotated session"}function Yn(){return`annotation-${Math.random().toString(36).slice(2,10)}`}function _(o){return!Number.isFinite(o)||o<=0?0:o>=1?1:o}function Se(o){return _(Math.max(.02,o))}function rt(o){return o?[String(o.taskId||"").trim(),String(o.assetId||"").trim(),String(o.path||"").trim()].join("::"):""}function Go(o){if(!(o instanceof HTMLElement))return!1;const l=o.tagName.toLowerCase();return o.isContentEditable?!0:l==="input"||l==="textarea"||l==="select"}function Ie(o){return o.map((l,m)=>({...l,order:m}))}function Qt(o){if(!o)return"Unsaved";const l=new Date(o);return Number.isNaN(l.getTime())?"Unsaved":l.toLocaleString()}function St(o){const l=String(o.createdByActor?.label||"").trim();return l||null}function Yo(o){const l=String(o||"").trim().replace(/\s+/g," ");return l?l.length>110?`${l.slice(0,107)}...`:l:""}function Ln(o,l,m,g=it[re]){const w={id:Yn(),order:0,instruction:"",markerType:re,color:g};if(o==="pin")return{...w,kind:o,x:l.x,y:l.y};if(o==="text-note")return{...w,kind:o,x:l.x,y:l.y};if(o==="box"){const M=m||l;return{...w,kind:o,x:_(Math.min(l.x,M.x)),y:_(Math.min(l.y,M.y)),width:Se(Math.abs(M.x-l.x)),height:Se(Math.abs(M.y-l.y))}}const y=m||l;return{...w,kind:"arrow",x:l.x,y:l.y,x2:y.x,y2:y.y}}function en(o){return o.color?o.color:it[o.markerType||re]}function Ko(o,l){const m=Math.max(l.width,1),g=Math.max(l.height,1),w=o.x*m,y=o.y*g,M=o.x2*m,ne=o.y2*g,Ue=M-w,Ge=ne-y,me=Math.hypot(Ue,Ge)||1,ie=Ue/me,Ye=Ge/me,Ne=Math.max(10,Math.min(16,me-2)),T=Ne*.62,Ke=M-ie*Ne,pe=ne-Ye*Ne,he=-Ye,se=ie;return{shaftX1:w,shaftY1:y,shaftX2:Ke,shaftY2:pe,headPoints:[`${M},${ne}`,`${Ke+he*T},${pe+se*T}`,`${Ke-he*T},${pe-se*T}`].join(" ")}}function Wo(o,l){return{...o,markerType:l,color:o.color||it[l]}}function Xo(o,l){return{...o,id:Yn(),order:l}}function Vo(o,l){const m=String(o||"").trim()||(l?ze(l):"Annotated session");return/\bcopy$/i.test(m)?`${m} 2`:`${m} copy`}function qo(o,l,m){if(l===m||l<0||m<0||l>=o.length||m>=o.length)return o;const g=[...o],[w]=g.splice(l,1);return w?(g.splice(m,0,w),Ie(g)):o}function te(o){return String(Math.round(_(o)*1e3)/10)}function Jo(o){const l=Number.parseFloat(o);return Number.isFinite(l)?_(l/100):null}function Zo(o,l,m){return o.kind==="pin"||o.kind==="text-note"?l==="x"||l==="y"?{...o,[l]:_(m)}:o:o.kind==="box"?l==="x"||l==="y"?{...o,[l]:_(m)}:l==="width"||l==="height"?{...o,[l]:Se(m)}:o:l==="x"||l==="y"||l==="x2"||l==="y2"?{...o,[l]:_(m)}:o}function Qo(o){return o.kind==="pin"||o.kind==="text-note"?[{key:"x",label:"X",value:te(o.x)},{key:"y",label:"Y",value:te(o.y)}]:o.kind==="box"?[{key:"x",label:"X",value:te(o.x)},{key:"y",label:"Y",value:te(o.y)},{key:"width",label:"Width",value:te(o.width)},{key:"height",label:"Height",value:te(o.height)}]:[{key:"x",label:"Start X",value:te(o.x)},{key:"y",label:"Start Y",value:te(o.y)},{key:"x2",label:"End X",value:te(o.x2)},{key:"y2",label:"End Y",value:te(o.y2)}]}function er(o){if(!o)return null;const l=Math.round(o.x*100),m=Math.round(o.y*100),g=Math.round(o.width*100),w=Math.round(o.height*100);return`crop ${l}%, ${m}% size ${g}% x ${w}%`}function tr(o){return Number.isFinite(o)?Math.min(4,Math.max(.25,Number(o.toFixed(2)))):1}function Fn(o){const l=[`Annotated attachment: ${o.title||o.image.displayName}`,`Image: ${o.image.displayName}`,`Image Reference: ${o.image.referenceLabel||o.image.assetId}`,`Task ID: ${o.taskId}`,o.globalInstruction?`Global instruction: ${o.globalInstruction}`:"Global instruction: None provided.","Markers:"];return o.annotations.length===0?(l.push("0. No markers."),l.join(`
|
|
1
|
+
import{j as t,r as a,R as qt}from"./vendor-react-CKJs5o3c.js";import{R as xs,f as Ce,e as Ct,g as vs,t as Bn,M as Jt}from"./index-xBWLPOEb.js";import{q as _s,m as js,c as Zt,T as Tn,s as ks,a1 as ws,h as An,ai as Cs,R as Is,aj as Ss,ak as Ns,al as $s,am as tn,C as nn,an as Rs,ao as Dn,ap as zn,aq as Un,ar as Gn,g as Ps,f as Bs,b as sn}from"./vendor-icons-QZyhEwgT.js";import"./vendor-markdown-BUxTU7dS.js";import"./vendor-dnd-DRzYolkg.js";import"./vendor-router-BbWMxlnO.js";function Ts({copied:o,disabled:l=!1,label:m,onClick:g,title:w="Copy image reference",ariaLabel:y,className:M=""}){return t.jsx(xs,{copied:o,disabled:l,label:"",onClick:g,title:w,ariaLabel:y,className:M,children:m})}const As="_shell_sf7jc_1",Es="_panel_sf7jc_16",Ms="_sessionPanel_sf7jc_22",Ls="_detailPanel_sf7jc_23",Fs="_sessionContextBar_sf7jc_29",Hs="_sessionContextLeft_sf7jc_40",Os="_sessionContextRight_sf7jc_41",Ds="_sessionContextLabel_sf7jc_56",zs="_sessionContextSpacer_sf7jc_62",Us="_canvasPanel_sf7jc_67",Gs="_canvasWorkspace_sf7jc_74",Ys="_canvasMain_sf7jc_81",Ks="_panelHeader_sf7jc_88",Ws="_panelHeaderText_sf7jc_97",Xs="_panelTitle_sf7jc_101",Vs="_canvasHeading_sf7jc_105",qs="_sessionActions_sf7jc_109",Js="_sessionList_sf7jc_117",Zs="_annotationList_sf7jc_118",Qs="_markerHelpModalBody_sf7jc_133",ea="_openImageModalBody_sf7jc_139",ta="_openImageField_sf7jc_145",na="_openImageActions_sf7jc_149",sa="_markerHelpItem_sf7jc_155",aa="_markerHelpHeader_sf7jc_163",oa="_markerHelpExample_sf7jc_175",ra="_sessionCard_sf7jc_179",ia="_annotationCard_sf7jc_180",la="_sessionEmptyState_sf7jc_197",ca="_sessionCardButton_sf7jc_204",da="_annotationCardButton_sf7jc_214",ua="_sessionCardBody_sf7jc_224",fa="_sessionCardActive_sf7jc_230",ma="_annotationCardActive_sf7jc_231",pa="_annotationMeta_sf7jc_241",ha="_annotationInstructionPreview_sf7jc_248",ba="_annotationPreviewFooter_sf7jc_256",ga="_annotationInstructionEditor_sf7jc_263",ya="_annotationTypeField_sf7jc_269",xa="_annotationInstructionButton_sf7jc_275",va="_annotationInstructionField_sf7jc_284",_a="_sessionMeta_sf7jc_288",ja="_sessionTitle_sf7jc_295",ka="_annotationTitle_sf7jc_296",wa="_sessionTimestamp_sf7jc_302",Ca="_annotationKind_sf7jc_303",Ia="_annotationInstructionTypeIcon_sf7jc_307",Sa="_sessionInstructionPreview_sf7jc_313",Na="_sessionInstructionEditor_sf7jc_321",$a="_sessionCardFooter_sf7jc_327",Ra="_toolRail_sf7jc_334",Pa="_canvasToolRail_sf7jc_343",Ba="_toolbarCluster_sf7jc_364",Ta="_toolbarViewportCluster_sf7jc_371",Aa="_toolbarSeparator_sf7jc_375",Ea="_toolBtn_sf7jc_381",Ma="_toolRailButton_sf7jc_385",La="_toolbarButton_sf7jc_395",Fa="_toolBtnActive_sf7jc_400",Ha="_toolbarActions_sf7jc_407",Oa="_toolbarSelectionActions_sf7jc_416",Da="_toolbarColorPicker_sf7jc_424",za="_colorPickerButton_sf7jc_428",Ua="_colorPickerSwatch_sf7jc_433",Ga="_colorPickerPopover_sf7jc_441",Ya="_colorOption_sf7jc_456",Ka="_colorOptionActive_sf7jc_466",Wa="_toolbarUtilities_sf7jc_473",Xa="_toolbarGeometryFields_sf7jc_481",Va="_toolbarGeometryField_sf7jc_481",qa="_toolbarGeometryLabel_sf7jc_494",Ja="_toolbarGeometryInput_sf7jc_500",Za="_iconButton_sf7jc_505",Qa="_ghostBtn_sf7jc_510",eo="_payloadBtn_sf7jc_511",to="_backToTaskBtn_sf7jc_512",no="_canvasScroller_sf7jc_532",so="_canvasFrame_sf7jc_548",ao="_canvasMedia_sf7jc_555",oo="_canvasStatusOverlay_sf7jc_563",ro="_canvasStatusCard_sf7jc_575",io="_canvasImage_sf7jc_588",lo="_overlay_sf7jc_595",co="_overlaySelect_sf7jc_601",uo="_overlayPan_sf7jc_605",fo="_overlaySvg_sf7jc_609",mo="_overlayHitLayer_sf7jc_618",po="_arrowHitArea_sf7jc_627",ho="_canvasHandleHit_sf7jc_634",bo="_canvasResizeHandleHit_sf7jc_641",go="_canvasHandleVisible_sf7jc_645",yo="_pin_sf7jc_662",xo="_note_sf7jc_663",vo="_annotationNumberBadge_sf7jc_680",_o="_box_sf7jc_705",jo="_boxNumberBadge_sf7jc_714",ko="_arrowNumberBadge_sf7jc_720",wo="_boxSurface_sf7jc_724",Co="_selected_sf7jc_737",Io="_textInput_sf7jc_754",So="_textArea_sf7jc_755",No="_select_sf7jc_737",$o="_sessionTitleInput_sf7jc_762",Ro="_sessionInstructionField_sf7jc_767",Po="_detailEmpty_sf7jc_776",Bo="_emptyState_sf7jc_777",To="_payloadModalBody_sf7jc_789",Ao="_payloadModalToolbar_sf7jc_796",Eo="_payloadViewToggle_sf7jc_803",Mo="_payloadModalActions_sf7jc_804",Lo="_payloadModalPreview_sf7jc_811",Fo="_statusBar_sf7jc_826",Ho="_annotationSummary_sf7jc_838",s={shell:As,panel:Es,sessionPanel:Ms,detailPanel:Ls,sessionContextBar:Fs,sessionContextLeft:Hs,sessionContextRight:Os,sessionContextLabel:Ds,sessionContextSpacer:zs,canvasPanel:Us,canvasWorkspace:Gs,canvasMain:Ys,panelHeader:Ks,panelHeaderText:Ws,panelTitle:Xs,canvasHeading:Vs,sessionActions:qs,sessionList:Js,annotationList:Zs,markerHelpModalBody:Qs,openImageModalBody:ea,openImageField:ta,openImageActions:na,markerHelpItem:sa,markerHelpHeader:aa,markerHelpExample:oa,sessionCard:ra,annotationCard:ia,sessionEmptyState:la,sessionCardButton:ca,annotationCardButton:da,sessionCardBody:ua,sessionCardActive:fa,annotationCardActive:ma,annotationMeta:pa,annotationInstructionPreview:ha,annotationPreviewFooter:ba,annotationInstructionEditor:ga,annotationTypeField:ya,annotationInstructionButton:xa,annotationInstructionField:va,sessionMeta:_a,sessionTitle:ja,annotationTitle:ka,sessionTimestamp:wa,annotationKind:Ca,annotationInstructionTypeIcon:Ia,sessionInstructionPreview:Sa,sessionInstructionEditor:Na,sessionCardFooter:$a,toolRail:Ra,canvasToolRail:Pa,toolbarCluster:Ba,toolbarViewportCluster:Ta,toolbarSeparator:Aa,toolBtn:Ea,toolRailButton:Ma,toolbarButton:La,toolBtnActive:Fa,toolbarActions:Ha,toolbarSelectionActions:Oa,toolbarColorPicker:Da,colorPickerButton:za,colorPickerSwatch:Ua,colorPickerPopover:Ga,colorOption:Ya,colorOptionActive:Ka,toolbarUtilities:Wa,toolbarGeometryFields:Xa,toolbarGeometryField:Va,toolbarGeometryLabel:qa,toolbarGeometryInput:Ja,iconButton:Za,ghostBtn:Qa,payloadBtn:eo,backToTaskBtn:to,canvasScroller:no,canvasFrame:so,canvasMedia:ao,canvasStatusOverlay:oo,canvasStatusCard:ro,canvasImage:io,overlay:lo,overlaySelect:co,overlayPan:uo,overlaySvg:fo,overlayHitLayer:mo,arrowHitArea:po,canvasHandleHit:ho,canvasResizeHandleHit:bo,canvasHandleVisible:go,pin:yo,note:xo,annotationNumberBadge:vo,box:_o,boxNumberBadge:jo,arrowNumberBadge:ko,boxSurface:wo,selected:Co,textInput:Io,textArea:So,select:No,sessionTitleInput:$o,sessionInstructionField:Ro,detailEmpty:Po,emptyState:Bo,payloadModalBody:To,payloadModalToolbar:Ao,payloadViewToggle:Eo,payloadModalActions:Mo,payloadModalPreview:Lo,statusBar:Fo,annotationSummary:Ho},En=[{value:"review",label:"Review"},{value:"change",label:"Change"},{value:"question",label:"Question"}],Mn=[{value:"select",label:"Select",icon:Rs},{value:"pin",label:"Pin",icon:Dn},{value:"box",label:"Box",icon:zn},{value:"arrow",label:"Arrow",icon:Un},{value:"text-note",label:"Note",icon:Gn}],Oo={pin:Dn,box:zn,arrow:Un,"text-note":Gn},Do={pin:"Pin",box:"Box",arrow:"Arrow","text-note":"Note"},zo={review:tn,change:sn,question:nn,issue:nn,idea:tn},It={select:{short:"Select and edit existing markers.",detail:"Use Select to click, drag, reorder, resize, and update markers that are already on the image.",example:"Example: move an existing marker after the screenshot changes."},pin:{short:"Mark a precise spot.",detail:"Use Pin when feedback points to one exact location instead of a broader area.",example:'Example: "This icon is misaligned by 2px."'},box:{short:"Mark an area or component.",detail:"Use Box when the feedback applies to a whole region, card, panel, or bounded UI block.",example:'Example: "This whole card needs tighter padding and a stronger border."'},arrow:{short:"Show direction or relationship.",detail:"Use Arrow when you need to show movement, attachment, flow, or source-to-target intent.",example:'Example: "This tooltip should anchor to this button, not the panel."'},"text-note":{short:"Add a comment-style point marker.",detail:"Use Note when you want a point marker that reads more like a comment or open question.",example:'Example: "Ask design whether this badge should stay."'}},it={question:"#0f766e",change:"#2563eb",issue:"#dc2626",idea:"#d97706",review:"#7c3aed"},Uo=["#7c3aed","#2563eb","#0f766e","#dc2626","#d97706","#111827"],re="review";function ze(o){const l=String(o.displayName||"").trim();return l?`${l} review`:"Annotated session"}function Yn(){return`annotation-${Math.random().toString(36).slice(2,10)}`}function _(o){return!Number.isFinite(o)||o<=0?0:o>=1?1:o}function Se(o){return _(Math.max(.02,o))}function rt(o){return o?[String(o.taskId||"").trim(),String(o.assetId||"").trim(),String(o.path||"").trim()].join("::"):""}function Go(o){if(!(o instanceof HTMLElement))return!1;const l=o.tagName.toLowerCase();return o.isContentEditable?!0:l==="input"||l==="textarea"||l==="select"}function Ie(o){return o.map((l,m)=>({...l,order:m}))}function Qt(o){if(!o)return"Unsaved";const l=new Date(o);return Number.isNaN(l.getTime())?"Unsaved":l.toLocaleString()}function St(o){const l=String(o.createdByActor?.label||"").trim();return l||null}function Yo(o){const l=String(o||"").trim().replace(/\s+/g," ");return l?l.length>110?`${l.slice(0,107)}...`:l:""}function Ln(o,l,m,g=it[re]){const w={id:Yn(),order:0,instruction:"",markerType:re,color:g};if(o==="pin")return{...w,kind:o,x:l.x,y:l.y};if(o==="text-note")return{...w,kind:o,x:l.x,y:l.y};if(o==="box"){const M=m||l;return{...w,kind:o,x:_(Math.min(l.x,M.x)),y:_(Math.min(l.y,M.y)),width:Se(Math.abs(M.x-l.x)),height:Se(Math.abs(M.y-l.y))}}const y=m||l;return{...w,kind:"arrow",x:l.x,y:l.y,x2:y.x,y2:y.y}}function en(o){return o.color?o.color:it[o.markerType||re]}function Ko(o,l){const m=Math.max(l.width,1),g=Math.max(l.height,1),w=o.x*m,y=o.y*g,M=o.x2*m,ne=o.y2*g,Ue=M-w,Ge=ne-y,me=Math.hypot(Ue,Ge)||1,ie=Ue/me,Ye=Ge/me,Ne=Math.max(10,Math.min(16,me-2)),T=Ne*.62,Ke=M-ie*Ne,pe=ne-Ye*Ne,he=-Ye,se=ie;return{shaftX1:w,shaftY1:y,shaftX2:Ke,shaftY2:pe,headPoints:[`${M},${ne}`,`${Ke+he*T},${pe+se*T}`,`${Ke-he*T},${pe-se*T}`].join(" ")}}function Wo(o,l){return{...o,markerType:l,color:o.color||it[l]}}function Xo(o,l){return{...o,id:Yn(),order:l}}function Vo(o,l){const m=String(o||"").trim()||(l?ze(l):"Annotated session");return/\bcopy$/i.test(m)?`${m} 2`:`${m} copy`}function qo(o,l,m){if(l===m||l<0||m<0||l>=o.length||m>=o.length)return o;const g=[...o],[w]=g.splice(l,1);return w?(g.splice(m,0,w),Ie(g)):o}function te(o){return String(Math.round(_(o)*1e3)/10)}function Jo(o){const l=Number.parseFloat(o);return Number.isFinite(l)?_(l/100):null}function Zo(o,l,m){return o.kind==="pin"||o.kind==="text-note"?l==="x"||l==="y"?{...o,[l]:_(m)}:o:o.kind==="box"?l==="x"||l==="y"?{...o,[l]:_(m)}:l==="width"||l==="height"?{...o,[l]:Se(m)}:o:l==="x"||l==="y"||l==="x2"||l==="y2"?{...o,[l]:_(m)}:o}function Qo(o){return o.kind==="pin"||o.kind==="text-note"?[{key:"x",label:"X",value:te(o.x)},{key:"y",label:"Y",value:te(o.y)}]:o.kind==="box"?[{key:"x",label:"X",value:te(o.x)},{key:"y",label:"Y",value:te(o.y)},{key:"width",label:"Width",value:te(o.width)},{key:"height",label:"Height",value:te(o.height)}]:[{key:"x",label:"Start X",value:te(o.x)},{key:"y",label:"Start Y",value:te(o.y)},{key:"x2",label:"End X",value:te(o.x2)},{key:"y2",label:"End Y",value:te(o.y2)}]}function er(o){if(!o)return null;const l=Math.round(o.x*100),m=Math.round(o.y*100),g=Math.round(o.width*100),w=Math.round(o.height*100);return`crop ${l}%, ${m}% size ${g}% x ${w}%`}function tr(o){return Number.isFinite(o)?Math.min(4,Math.max(.25,Number(o.toFixed(2)))):1}function Fn(o){const l=[`Annotated attachment: ${o.title||o.image.displayName}`,`Image: ${o.image.displayName}`,`Image Reference: ${o.image.referenceLabel||o.image.assetId}`,`Task ID: ${o.taskId}`,o.globalInstruction?`Global instruction: ${o.globalInstruction}`:"Global instruction: None provided.","Markers:"];return o.annotations.length===0?(l.push("0. No markers."),l.join(`
|
|
2
2
|
`)):(o.annotations.forEach((m,g)=>{const w=m.markerType||re,y=er(m.cropHint);l.push(`${g+1}. ${m.kind} (${w})`),l.push(`Instruction: ${m.instruction||"No marker instruction."}`),y&&l.push(`Region: ${y}`)}),l.join(`
|
|
3
3
|
`))}function Nt(o){return{title:o.title,globalInstruction:o.globalInstruction,annotations:Ie(o.annotations)}}function Hn(o){return JSON.stringify(Nt(o))}function On(o){const l=JSON.parse(o);return Nt({title:String(l?.title||""),globalInstruction:String(l?.globalInstruction||""),annotations:Array.isArray(l?.annotations)?l.annotations:[]})}function nr(o,l){return Nt({title:o?.title||l,globalInstruction:o?.globalInstruction||"",annotations:o?.annotations||[]})}function dr({runtimeMode:o="local",apiBaseUrl:l="",cloudAuthBaseUrl:m="",workspaceId:g="default",sessionLoadReady:w=!0,requestedTarget:y=null,requestedSessionId:M=null,requestedOpenVersion:ne=0,resolveTaskReferenceLabel:Ue,resolveImageReferenceLabel:Ge,onRequestedTargetHandled:me,onOpenTarget:ie,onContextChange:Ye,onBackToTask:Ne}){const T=o==="cloud"&&(m||l)||"",[Ke,pe]=a.useState(y),[he,se]=a.useState([]),[x,$e]=a.useState(null),[$t,We]=a.useState(!1),[Re,Xe]=a.useState(""),[Pe,Ve]=a.useState(""),[N,ae]=a.useState([]),[C,L]=a.useState(null),[lt,an]=a.useState(it[re]),[ct,dt]=a.useState(!1),[$,Be]=a.useState("select"),[Kn,be]=a.useState(!1),[ut,on]=a.useState(!1),[W,X]=a.useState(!1),[rn,h]=a.useState(null),[U,R]=a.useState("saved"),[sr,D]=a.useState(null),[ft,ln]=a.useState(!1),[oe,Rt]=a.useState(null),[le,Pt]=a.useState(!1),[Wn,Bt]=a.useState(!1),[Tt,cn]=a.useState("json"),[mt,At]=a.useState(!1),[pt,Et]=a.useState(!1),[Xn,dn]=a.useState(!1),[Vn,ht]=a.useState(!1),[Mt,bt]=a.useState(""),[qe,un]=a.useState(!1),[ce,Je]=a.useState(!1),[Lt,Ze]=a.useState(!1),[P,Ft]=a.useState(1),[G,Qe]=a.useState(!1),[gt,fn]=a.useState(!1),[yt,et]=a.useState({width:0,height:0}),mn=a.useRef(null),Te=a.useRef(null),Ht=a.useRef(null),pn=a.useRef(null),ge=a.useRef(M),ye=a.useRef(""),xt=a.useRef(0),Ot=a.useRef(""),xe=a.useRef(0),vt=a.useRef(null),hn=a.useRef(0),de=a.useRef(!1),tt=a.useRef(null),Dt=a.useRef(null),ve=a.useRef(null),F=a.useRef(""),A=a.useRef(""),_t=a.useRef(null),V=a.useRef(null),Ae=a.useRef(!1),jt=a.useRef(null),nt=a.useRef(null),st=a.useRef(null),_e=a.useRef(null),Ee=a.useRef(null),Me=a.useRef(null),Le=a.useRef(null),ue=a.useRef(null),q=a.useRef(null),bn=a.useRef(null),gn=a.useRef(null),B=a.useMemo(()=>he.find(e=>e.id===x)||null,[x,he]),J=a.useMemo(()=>`workspaceId=${encodeURIComponent(String(g||"default").trim()||"default")}`,[g]),E=a.useMemo(()=>({"x-taskforce-workspace-id":String(g||"default").trim()||"default"}),[g]),z=a.useMemo(()=>N.find(e=>e.id===C)||null,[N,C]),Z=a.useMemo(()=>rt(y),[y]),je=typeof ie=="function",d=je?y:Ke,kt=a.useMemo(()=>{const e=String(d?.taskId||"").trim();if(!e)return"";const n=Ue?.(e).trim()||"";if(n)return n;const r=String(d?.taskReferenceLabel||"").trim();return r&&r!==e?r:n||e},[d?.taskId,d?.taskReferenceLabel,Ue]),at=a.useMemo(()=>{const e=String(d?.assetId||"").trim();if(!e)return"";const n=Ge?.(e).trim()||"";return n||String(d?.imageReferenceLabel||"").trim()},[d?.assetId,d?.imageReferenceLabel,Ge]),zt=a.useMemo(()=>N.findIndex(e=>e.id===C),[N,C]),Ut=z?.color||lt,yn=a.useMemo(()=>Nt({title:Re,globalInstruction:Pe,annotations:N}),[N,Pe,Re]),Fe=a.useMemo(()=>Hn(yn),[yn]),xn=Fe!==A.current,b=a.useMemo(()=>({width:Math.max(yt.width*P,0),height:Math.max(yt.height*P,0)}),[yt.height,yt.width,P]),He=a.useMemo(()=>({visibleRadius:7,hitRadius:11}),[]),Gt=a.useMemo(()=>`0 0 ${Math.max(b.width,1)} ${Math.max(b.height,1)}`,[b.height,b.width]),ke=a.useMemo(()=>{const e=Te.current;return e?P>1||b.width>e.clientWidth+1||b.height>e.clientHeight+1:P>1},[b.height,b.width,P]),Yt=`${Math.round(P*100)}%`,Kt=a.useMemo(()=>{const e=new Map;return N.forEach((n,r)=>{e.set(n.id,r+1)}),e},[N]);a.useEffect(()=>{ge.current=M},[M]),a.useEffect(()=>{const e=pn.current;if(!e||!C)return;e.focus();const n=e.value.length;e.setSelectionRange(n,n)},[C]),a.useEffect(()=>{ve.current=x},[x]),a.useEffect(()=>{We(!1)},[x]),a.useEffect(()=>{if(!z){dt(!1);return}an(z.color||it[z.markerType||re])},[z]);const wt=a.useCallback(async e=>{const n=typeof performance<"u"?performance.now():Date.now(),r=await Ce(`/api/taskforce/annotated-attachments/sessions?${J}`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",...E},body:JSON.stringify({taskId:e.taskId,baseImageAssetId:e.assetId,title:ze(e),globalInstruction:"",annotations:[]})},T),i=await r.json().catch(()=>({}));if(!r.ok)throw new Error(String(i?.error||"Failed to create session."));const c=i?.session;if(!c?.id)throw new Error("Failed to create session.");return Ct("annotated_session_create_completed",{assetId:e.assetId,taskId:e.taskId||null,sessionId:c.id,durationMs:Math.round((typeof performance<"u"?performance.now():Date.now())-n),debugTimings:i?.debugTimings||null,serverTiming:typeof r.headers?.get=="function"&&r.headers.get("server-timing")||null}),c},[E,T,J]),vn=a.useCallback(e=>new Promise((n,r)=>{const i=new FileReader;i.onload=()=>n(typeof i.result=="string"?i.result:""),i.onerror=()=>r(i.error||new Error("Failed to read clipboard image.")),i.readAsDataURL(e)}),[]),I=a.useCallback(()=>{tt.current!==null&&(window.clearTimeout(tt.current),tt.current=null),st.current!==null&&(window.clearTimeout(st.current),st.current=null)},[]),Oe=a.useCallback(e=>{Xe(e.title),Ve(e.globalInstruction),ae(e.annotations),L(n=>n&&e.annotations.some(r=>r.id===n)?n:e.annotations[0]?.id||null)},[]),fe=a.useCallback((e,n)=>{const r=ze(n||d||{assetId:e.baseImageAssetId,displayName:"Annotated session"}),i=nr(e,r),c=Hn(i);de.current=!0,I(),Oe(i),A.current=c,F.current=c,_t.current=null,Ae.current=!1,jt.current=null,nt.current=null,D(null),R("saved"),X(!1)},[d,Oe,I]),Wt=a.useCallback(e=>{de.current=!0,I(),se([]),$e(null),be(!1),We(!1),Xe(ze(e)),Ve(""),ae([]),L(null),A.current="",F.current="",_t.current=null,Ae.current=!1,jt.current=null,nt.current=null,Rt(null),Bt(!1),D(null),R("saved"),X(!1)},[I]),we=a.useCallback(async(e,n,r)=>{const i=ve.current;if(!i)return!0;if(e===A.current)return V.current||(D(null),R("saved")),!0;if(V.current){if(Ae.current=!0,!r?.waitForInFlight||!await V.current)return!1;const f=F.current;return f===A.current?!0:we(f,n,r)}I();const c=On(e);jt.current=i,_t.current=e,X(!0),h(null),D(null),R("saving");const p=(async()=>{try{const u=await Ce(`/api/taskforce/annotated-attachments/sessions/${encodeURIComponent(i)}?${J}`,{method:"PATCH",credentials:"include",headers:{"Content-Type":"application/json",...E},body:JSON.stringify(c)},T),f=await u.json().catch(()=>({}));if(!u.ok)throw new Error(String(f?.error||"Failed to save session."));const v=f?.session;if(!v?.id)throw new Error("Failed to save session.");se(Vt=>{const Rn=Vt.findIndex(ys=>ys.id===v.id);if(Rn===-1)return[v,...Vt];const Pn=[...Vt];return Pn[Rn]=v,Pn}),A.current=e,nt.current=null,D(null);const k=ve.current===v.id,S=F.current,K=S!==e,ot=Ae.current||K;return Ae.current=!1,k&&!ot?(de.current=!0,Oe(c),R("saved")):!ot&&S===A.current?R("saved"):R("pending"),!0}catch(u){const f=u instanceof Error?u.message:"Failed to save session.";return h(f),D(f),R("error"),nt.current!==e&&ve.current===i&&F.current===e&&(nt.current=e,st.current=window.setTimeout(()=>{st.current=null,!(ve.current!==i||F.current!==e)&&we(e,n,{waitForInFlight:!0})},1500)),!1}finally{_t.current=null,V.current=null,jt.current=null,X(!1)}})();V.current=p;const j=await p;if(j){const u=F.current;if(u!==A.current)return we(u,n,r)}return j},[Oe,I,E,T,J]),_n=a.useCallback(e=>{if(ve.current){if(F.current===A.current){D(null),V.current||R("saved");return}U!=="saving"&&R("pending"),I(),tt.current=window.setTimeout(()=>{tt.current=null,we(F.current,"structure")},e)}},[I,we,U]),Y=a.useCallback(e=>{Dt.current=e},[]),H=a.useCallback(async e=>{I();const n=F.current;return!ve.current||n===A.current?(D(null),V.current||R("saved"),!0):we(n,e,{waitForInFlight:!0})},[I,we]),qn=a.useCallback(()=>{const e=A.current;e&&(I(),de.current=!0,Oe(On(e)),D(null),R("saved"),h(null))},[Oe,I]),Q=a.useCallback(async(e,n)=>{const r=typeof performance<"u"?performance.now():Date.now(),i=JSON.stringify({targetKey:rt(e),requestedSessionId:n?.requestedSessionId??null,autoCreateIfEmpty:n?.autoCreateIfEmpty===!0});if(vt.current===i)return;const c=xe.current+1;xe.current=c,vt.current=i,on(!0),h(null);try{const p=new URLSearchParams;g&&p.set("workspaceId",g),e.taskId&&p.set("taskId",e.taskId),p.set("imageAssetId",e.assetId);const j=await Ce(`/api/taskforce/annotated-attachments/sessions?${p.toString()}`,{credentials:"include",headers:E,cache:"no-store"},T),u=await j.json().catch(()=>({}));if(!j.ok)throw new Error(String(u?.error||"Failed to load annotated attachment sessions."));const f=Array.isArray(u?.sessions)?u.sessions:[];if(Ct("annotated_sessions_loaded",{assetId:e.assetId,taskId:e.taskId||null,requestedSessionId:n?.requestedSessionId??null,autoCreateIfEmpty:n?.autoCreateIfEmpty===!0,sessionCount:f.length,durationMs:Math.round((typeof performance<"u"?performance.now():Date.now())-r),serverTiming:typeof j.headers?.get=="function"&&j.headers.get("server-timing")||null}),f.length===0&&n?.autoCreateIfEmpty&&String(e.assetId||"").trim()){const S=n?.requestedSessionId??ge.current;if(ge.current=null,S&&h("The previously selected annotation session could not be restored."),xe.current!==c)return;const K=await wt(e);if(Ct("annotated_sessions_auto_created_after_empty_load",{assetId:e.assetId,taskId:e.taskId||null,sessionId:K.id,totalDurationMs:Math.round((typeof performance<"u"?performance.now():Date.now())-r)}),xe.current!==c)return;se([K]),$e(K.id),be(!1),fe(K,e);return}if(xe.current!==c)return;se(f),be(f.length===0);const v=n?.requestedSessionId??ge.current;ge.current=null;const k=f.find(S=>S.id===v)||f[0]||null;v&&!k&&h("The previously selected annotation session could not be restored."),$e(k?.id||null),k?fe(k,e):(de.current=!0,I(),Xe(ze(e)),Ve(""),ae([]),L(null),A.current="",F.current="",D(null),R("saved"))}catch(p){if(xe.current!==c)return;h(p instanceof Error?p.message:"Failed to load sessions.")}finally{vt.current===i&&(vt.current=null),xe.current===c&&on(!1)}},[I,wt,E,T,fe,g]),Jn=a.useCallback(async()=>{if(typeof navigator>"u"||!navigator.clipboard||typeof navigator.clipboard.read!="function"){h("Clipboard image paste is not supported in this environment.");return}fn(!0),h(null),hn.current=Date.now()+2e3;try{const n=(await navigator.clipboard.read()).find(v=>v.types.some(k=>k.startsWith("image/"))),r=n?.types.find(v=>v.startsWith("image/"))||"";if(!n||!r)throw new Error("No image found on the clipboard.");const i=await n.getType(r),c=await vn(i);if(!c)throw new Error("Failed to read clipboard image.");const p=r==="image/jpeg"?"jpg":r==="image/webp"?"webp":r==="image/gif"?"gif":"png",j=await fetch("/api/taskforce/context-upload",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",...E},body:JSON.stringify({file:c,originalName:`pasted-image.${p}`,workspaceId:String(g||"default").trim()||"default"})}),u=await j.json().catch(()=>({}));if(!j.ok||!u?.success||typeof u?.assetId!="string"||typeof u?.path!="string")throw new Error(String(u?.error||"Failed to paste image into Image Notes."));const f={assetId:u.assetId,imageReferenceLabel:typeof u?.referenceLabel=="string"?u.referenceLabel:void 0,path:u.path,displayName:typeof u?.displayName=="string"&&u.displayName.trim().length>0?u.displayName.trim():"Pasted image"};ge.current=null,je?(ye.current="",ie?.(f,{sessionId:null})):(pe(f),ye.current=rt(f),Q(f,{autoCreateIfEmpty:!0}))}catch(e){h(e instanceof Error?e.message:"Failed to paste image.")}finally{fn(!1)}},[je,Q,ie,vn,E,g]),Zn=a.useCallback(async()=>{const e=Mt.trim();if(!e){h("Enter an image reference to open.");return}un(!0),h(null);try{const n=new URLSearchParams({workspaceId:String(g||"default").trim()||"default"}),r=await Ce(`/api/taskforce/annotated-attachments/images/${encodeURIComponent(e)}?${n.toString()}`,{method:"GET",credentials:"include",headers:E}),i=await r.json().catch(()=>({}));if(!r.ok||!i?.target||typeof i.target.assetId!="string"||typeof i.target.path!="string")throw new Error(String(i?.error||"Failed to open image reference."));const c={assetId:i.target.assetId,path:i.target.path,displayName:typeof i.target.displayName=="string"&&i.target.displayName.trim().length>0?i.target.displayName.trim():"Image attachment",taskId:typeof i.target.taskId=="string"&&i.target.taskId.trim().length>0?i.target.taskId.trim():void 0,taskReferenceLabel:typeof i.target.taskReferenceLabel=="string"&&i.target.taskReferenceLabel.trim().length>0?i.target.taskReferenceLabel.trim():void 0,imageReferenceLabel:typeof i.target.imageReferenceLabel=="string"&&i.target.imageReferenceLabel.trim().length>0?i.target.imageReferenceLabel.trim():void 0};ge.current=null,ht(!1),bt(""),je?(ye.current="",ie?.(c,{sessionId:null})):(pe(c),ye.current=rt(c),Q(c,{autoCreateIfEmpty:!0}))}catch(n){h(n instanceof Error?n.message:"Failed to open image reference.")}finally{un(!1)}},[je,Q,ie,Mt,E,g]),jn=a.useCallback(e=>{se(n=>{const r=n.findIndex(c=>c.id===e.id);if(r===-1)return[e,...n];const i=[...n];return i[r]=e,i}),$e(e.id),fe(e,d)},[d,fe]),kn=a.useCallback(e=>{se(n=>{const r=n.filter(c=>c.id!==e),i=r[0]||null;return $e(i?.id||null),i?fe(i,d):(de.current=!0,I(),Xe(ze(d||{displayName:"Annotated session"})),Ve(""),ae([]),L(null),A.current="",F.current="",D(null),R("saved")),r})},[d,I,fe]);a.useEffect(()=>{if(!y)return;if(je||pe(n=>n&&rt(n)===Z&&n.taskReferenceLabel===y.taskReferenceLabel&&n.imageReferenceLabel===y.imageReferenceLabel&&n.displayName===y.displayName?n:y),!w){Z&&(Z!==ye.current||ne!==xt.current)&&Z!==Ot.current&&(Wt(y),Ot.current=Z,xt.current=ne,Ct("annotated_sessions_load_deferred",{assetId:y.assetId,taskId:y.taskId||null,requestedTargetKey:Z})),me?.();return}Z&&(Z!==ye.current||ne!==xt.current)&&(Wt(y),ye.current=Z,xt.current=ne,Ot.current="",Q(y,{autoCreateIfEmpty:!0})),me?.()},[je,Q,me,ne,y,Z,Wt,M,w]),a.useEffect(()=>{d&&Ye?.({target:d,sessionId:x})},[d,Ye,x]),a.useEffect(()=>{if(!d||typeof document>"u"||!w)return;const e=()=>{Date.now()<hn.current||F.current!==A.current||W||ut||Q(d,{requestedSessionId:x})},n=()=>{document.visibilityState==="visible"&&e()};return document.addEventListener("visibilitychange",n),()=>{document.removeEventListener("visibilitychange",n)}},[d,Q,ut,W,x,w]),a.useEffect(()=>{if(!d?.path){Je(!1),Ze(!1),et({width:0,height:0});return}Je(!0),Ze(!1),et({width:0,height:0}),Ft(1),Qe(!1)},[d?.path]),a.useEffect(()=>{if(!ce)return;const e=Ht.current;!e||!e.complete||e.naturalWidth<=0||e.naturalHeight<=0||(et({width:e.naturalWidth,height:e.naturalHeight}),Je(!1),Ze(!1))},[d?.path,ce]),a.useEffect(()=>{ke||Qe(!1)},[ke]),a.useEffect(()=>{Rt(null)},[x]),a.useEffect(()=>{if(F.current=Fe,de.current){de.current=!1;return}if(!x){I(),D(null),R("saved");return}if(Fe===A.current){I(),V.current||(D(null),R("saved"));return}const e=Dt.current;if(Dt.current=null,U==="error"&&e===null)return;const n=e??600;if(V.current){Ae.current=!0,R("pending");return}_n(n)},[I,Fe,U,_n,x]),a.useEffect(()=>{x&&(V.current||Fe===A.current&&U!=="error"&&U!=="saved"&&(D(null),R("saved")))},[Fe,U,x]),a.useEffect(()=>{if(typeof window>"u")return;const e=n=>{F.current!==A.current&&(n.preventDefault(),n.returnValue="")};return window.addEventListener("beforeunload",e),()=>window.removeEventListener("beforeunload",e)},[]),a.useEffect(()=>{if(typeof document>"u")return;const e=()=>{document.visibilityState==="hidden"&&H("visibility-hidden")};return document.addEventListener("visibilitychange",e),()=>document.removeEventListener("visibilitychange",e)},[H]),a.useEffect(()=>{if(!ct||typeof document>"u")return;const e=n=>{const r=n.target;r instanceof Node&&(bn.current?.contains(r)||dt(!1))};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[ct]),a.useEffect(()=>{if(!$t||typeof document>"u")return;const e=n=>{const r=n.target;r instanceof Node&&(gn.current?.contains(r)||We(!1))};return document.addEventListener("mousedown",e),()=>document.removeEventListener("mousedown",e)},[$t]),a.useEffect(()=>()=>{I()},[I]),a.useEffect(()=>{if(!le)return;const e=window.setTimeout(()=>Pt(!1),2e3);return()=>window.clearTimeout(e)},[le]),a.useEffect(()=>{if(!mt)return;const e=window.setTimeout(()=>At(!1),2e3);return()=>window.clearTimeout(e)},[mt]),a.useEffect(()=>{if(!pt)return;const e=window.setTimeout(()=>Et(!1),2e3);return()=>window.clearTimeout(e)},[pt]);const ee=a.useCallback((e,n,r=300)=>{ae(i=>Ie(i.map(c=>c.id===e?n(c):c))),Y(r)},[Y]),Qn=a.useCallback(e=>{z&&(ee(z.id,n=>({...n,color:e})),an(e),dt(!1))},[z,ee]),wn=a.useCallback(async()=>{if(!(!d||!await H("session-switch"))){X(!0),h(null);try{const n=await wt(d);be(!1),await Q(d,{requestedSessionId:n.id,autoCreateIfEmpty:!1}),Be("select")}catch(n){h(n instanceof Error?n.message:"Failed to create session."),be(!0)}finally{X(!1)}}},[d,wt,H,Q]),es=a.useCallback(e=>{if(e!=="select"&&!B){Qe(!1),h("No session exists for this image yet."),be(!0);return}h(null),be(!1),Qe(!1),Be(e)},[B]),ts=a.useCallback(async()=>{if(!(!d||!B||!await H("duplicate"))){X(!0),h(null);try{const n=Ie(N.map((p,j)=>Xo(p,j))),r=await Ce(`/api/taskforce/annotated-attachments/sessions?${J}`,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json",...E},body:JSON.stringify({taskId:d.taskId,baseImageAssetId:d.assetId,title:Vo(Re||B.title,d),globalInstruction:Pe,annotations:n})},T),i=await r.json().catch(()=>({}));if(!r.ok)throw new Error(String(i?.error||"Failed to duplicate session."));const c=i?.session;if(!c?.id)throw new Error("Failed to duplicate session.");jn(c),Be("select")}catch(n){h(n instanceof Error?n.message:"Failed to duplicate session.")}finally{X(!1)}}},[d,N,Pe,Re,jn,H,E,T,B,J]),ns=a.useCallback(async()=>H("manual"),[H]),Cn=a.useCallback(async()=>{if(x){ln(!0),h(null);try{const e=await Ce(`/api/taskforce/annotated-attachments/sessions/${encodeURIComponent(x)}/payload?${J}`,{credentials:"include",headers:E},T),n=await e.json().catch(()=>({}));if(!e.ok)throw new Error(String(n?.error||"Failed to load payload preview."));Rt(n?.payload||null)}catch(e){h(e instanceof Error?e.message:"Failed to load payload preview.")}finally{ln(!1)}}},[E,T,x,J]),ss=a.useCallback(()=>{x&&H("payload-preview").then(e=>{e&&(Bt(!0),Cn())})},[H,Cn,x]),In=a.useCallback(async e=>{if(!oe||!navigator.clipboard||typeof navigator.clipboard.writeText!="function"){h("Clipboard copy is not available in this browser.");return}try{const n=e==="json"?JSON.stringify(oe,null,2):Fn(oe);await navigator.clipboard.writeText(n),Pt(e)}catch(n){h(n instanceof Error?n.message:"Failed to copy payload content."),Pt(!1)}},[oe]),as=a.useCallback(async()=>{if(!kt||!navigator.clipboard||typeof navigator.clipboard.writeText!="function"){h("Clipboard copy is not available in this browser.");return}try{await navigator.clipboard.writeText(kt),At(!0)}catch(e){h(e instanceof Error?e.message:"Failed to copy task id."),At(!1)}},[kt]),os=a.useCallback(async()=>{if(!at||!navigator.clipboard||typeof navigator.clipboard.writeText!="function"){h("Clipboard copy is not available in this browser.");return}try{await navigator.clipboard.writeText(at),Et(!0)}catch(e){h(e instanceof Error?e.message:"Failed to copy image reference."),Et(!1)}},[at]),rs=a.useCallback(async()=>{if(!x||!await H("delete-session"))return;const n=(B?.title||"Untitled session").trim()||"Untitled session";if(window.confirm(`Delete the annotated attachment session "${n}"?`)){X(!0),h(null);try{const r=await Ce(`/api/taskforce/annotated-attachments/sessions/${encodeURIComponent(x)}?${J}`,{method:"DELETE",credentials:"include",headers:E},T),i=await r.json().catch(()=>({}));if(!r.ok)throw new Error(String(i?.error||"Failed to delete session."));kn(x),Be("select")}catch(r){h(r instanceof Error?r.message:"Failed to delete session.")}finally{X(!1)}}},[H,kn,E,T,B,x,J]),Xt=a.useCallback(()=>{C&&(ae(e=>Ie(e.filter(n=>n.id!==C))),L(null),Y(300))},[Y,C]),De=a.useCallback(e=>{const n=tr(e),r=Te.current;if(!r||n===P){Ft(n);return}const i=(r.scrollLeft+r.clientWidth/2)*(n/P)-r.clientWidth/2,c=(r.scrollTop+r.clientHeight/2)*(n/P)-r.clientHeight/2;Ft(n),window.requestAnimationFrame(()=>{r.scrollLeft=Math.max(0,i),r.scrollTop=Math.max(0,c)})},[P]),is=a.useCallback(()=>{De(P+.25)},[De,P]),ls=a.useCallback(()=>{De(P-.25)},[De,P]),cs=a.useCallback(()=>{De(1);const e=Te.current;e&&window.requestAnimationFrame(()=>{e.scrollLeft=0,e.scrollTop=0})},[De]),Sn=a.useCallback(e=>{C&&ae(n=>{const r=n.findIndex(c=>c.id===C);if(r===-1)return n;const i=e==="up"?r-1:r+1;return i<0||i>=n.length?n:(Y(300),qo(n,r,i))})},[Y,C]);a.useEffect(()=>{if(!C)return;const e=n=>{n.key==="Delete"&&(Go(n.target)||(n.preventDefault(),Xt()))};return window.addEventListener("keydown",e),()=>window.removeEventListener("keydown",e)},[Xt,C]);const O=a.useCallback(e=>{const n=mn.current?.getBoundingClientRect();return!n||n.width<=0||n.height<=0?null:{x:_((e.clientX-n.left)/n.width),y:_((e.clientY-n.top)/n.height)}},[]),ds=a.useCallback(e=>{if(G&&ke){const i=Te.current;if(!i)return;q.current={pointerId:e.pointerId,startX:e.clientX,startY:e.clientY,scrollLeft:i.scrollLeft,scrollTop:i.scrollTop},e.currentTarget.setPointerCapture(e.pointerId),e.preventDefault();return}if(!x)return;if($==="select"){L(null);return}const n=O(e);if(!n)return;if($==="pin"||$==="text-note"){const i=Ln($,n,n,lt);ae(c=>Ie([...c,i])),L(i.id),Y(300),Be("select");return}_e.current=n;const r=Ln($,n,n,lt);ue.current={annotationId:r.id,kind:$},e.currentTarget.setPointerCapture?.(e.pointerId),ae(i=>Ie([...i,r])),L(r.id),Y(300)},[ke,lt,Y,G,O,x,$]),us=a.useCallback(e=>{if(q.current?.pointerId===e.pointerId){e.currentTarget.releasePointerCapture?.(e.pointerId);return}if(!_e.current||!ue.current||$!=="box"&&$!=="arrow")return;const n=O(e);_e.current=null,ue.current=null,e.currentTarget.releasePointerCapture?.(e.pointerId),n&&Be("select")},[O,$]),Nn=a.useCallback((e,n)=>{if($!=="select"||G)return;const r=O(e);r&&(Ee.current={annotationId:n.id,originPointer:r,originAnnotation:n},L(n.id),e.stopPropagation())},[G,O,$]),fs=a.useCallback(e=>{if(q.current?.pointerId===e.pointerId){const u=Te.current;if(!u)return;const f=e.clientX-q.current.startX,v=e.clientY-q.current.startY;u.scrollLeft=q.current.scrollLeft-f,u.scrollTop=q.current.scrollTop-v;return}if(ue.current&&_e.current){const u=O(e);if(!u)return;const{annotationId:f,kind:v}=ue.current,k=_e.current;ee(f,S=>v==="box"&&S.kind==="box"?{...S,x:_(Math.min(k.x,u.x)),y:_(Math.min(k.y,u.y)),width:Se(Math.abs(u.x-k.x)),height:Se(Math.abs(u.y-k.y))}:v==="arrow"&&S.kind==="arrow"?{...S,x:k.x,y:k.y,x2:u.x,y2:u.y}:S);return}if(Me.current){const u=O(e);if(!u)return;const{annotationId:f,originPointer:v,originAnnotation:k}=Me.current,S=u.x-v.x,K=u.y-v.y;ee(f,()=>({...k,width:Se(k.width+S),height:Se(k.height+K)}));return}if(Le.current){const u=O(e);if(!u)return;const{annotationId:f,endpoint:v,originPointer:k,originAnnotation:S}=Le.current,K=u.x-k.x,ot=u.y-k.y;ee(f,()=>v==="tail"?{...S,x:_(S.x+K),y:_(S.y+ot)}:{...S,x2:_(S.x2+K),y2:_(S.y2+ot)});return}if(!Ee.current)return;const n=O(e);if(!n)return;const{annotationId:r,originPointer:i,originAnnotation:c}=Ee.current,p=n.x-i.x,j=n.y-i.y;ee(r,()=>c.kind==="pin"||c.kind==="text-note"?{...c,x:_(c.x+p),y:_(c.y+j)}:c.kind==="box"?{...c,x:_(c.x+p),y:_(c.y+j)}:{...c,x:_(c.x+p),y:_(c.y+j),x2:_(c.x2+p),y2:_(c.y2+j)})},[O,ee]),ms=a.useCallback(()=>{_e.current=null,Ee.current=null,Me.current=null,Le.current=null,ue.current=null,q.current=null},[]),ps=a.useCallback(()=>{_e.current=null,Ee.current=null,Me.current=null,Le.current=null,ue.current=null,q.current=null},[]),hs=a.useCallback(()=>{Ee.current=null,Me.current=null,Le.current=null,ue.current=null,q.current=null},[]),bs=a.useCallback((e,n)=>{if($!=="select"||G)return;const r=O(e);r&&(Me.current={annotationId:n.id,originPointer:r,originAnnotation:n},L(n.id),e.stopPropagation())},[G,O,$]),$n=a.useCallback((e,n,r)=>{if($!=="select"||G)return;const i=O(e);i&&(Le.current={annotationId:n.id,originPointer:i,originAnnotation:n,endpoint:r},L(n.id),e.stopPropagation())},[G,O,$]),gs=B?.updatedAt?Qt(B.updatedAt):"Not saved yet";return t.jsxs("div",{className:s.shell,children:[t.jsxs("section",{className:`tf-surface-panel ${s.panel} ${s.sessionPanel}`,children:[t.jsx("div",{className:s.sessionContextBar,children:d?.taskId?t.jsxs(t.Fragment,{children:[t.jsx("div",{className:s.sessionContextLeft,children:Ne?t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.backToTaskBtn}`,onClick:()=>{H("back-to-task").then(e=>{e&&d.taskId&&Ne(d.taskId)})},"aria-label":"Back to task",title:"Back to task",children:t.jsx(_s,{size:16})}):t.jsx("div",{className:s.sessionContextSpacer,"aria-hidden":"true"})}),t.jsx("div",{className:s.sessionContextRight,children:t.jsx(vs,{copied:mt,onClick:()=>{as()},title:"Copy task id",ariaLabel:mt?"Copied task id":"Copy task id",label:kt})})]}):t.jsxs(t.Fragment,{children:[t.jsx("div",{className:s.sessionContextLeft,children:t.jsx("span",{className:`tf-label-micro ${s.sessionContextLabel}`,children:"Unattached Image"})}),t.jsx("div",{className:s.sessionContextRight,children:t.jsx("div",{className:s.sessionContextSpacer,"aria-hidden":"true"})})]})}),t.jsxs("div",{className:s.panelHeader,children:[t.jsxs("div",{className:s.panelHeaderText,children:[t.jsx("div",{className:`tf-heading-card ${s.panelTitle}`,children:"Sessions"}),d?null:t.jsx("div",{className:"tf-text-secondary",children:"Open an image attachment to begin"})]}),t.jsx("div",{className:s.sessionActions,children:t.jsx("button",{type:"button",className:`tf-control-icon ${s.iconButton}`,onClick:()=>{wn()},disabled:!d||W,"aria-label":"Create session",title:"Create session",children:t.jsx(js,{size:16})})})]}),d?ut?t.jsx("div",{className:s.emptyState,children:t.jsx("p",{className:"tf-empty-copy",children:"Loading sessions…"})}):t.jsx("div",{className:`tf-scrollbar ${s.sessionList}`,children:he.length===0?t.jsxs("div",{className:s.sessionEmptyState,children:[t.jsx("div",{className:`tf-heading-card ${s.sessionTitle}`,children:"No sessions yet"}),t.jsx("div",{className:`tf-text-secondary ${s.annotationSummary}`,children:"Create the first annotation session for this image."})]}):he.map(e=>{const n=x===e.id,r=n&&$t,i=Yo(n?Pe:e.globalInstruction),c=(n?Re:e.title)||"Untitled session";return t.jsxs("div",{className:`tf-surface-elevated ${s.sessionCard} ${n?s.sessionCardActive:""}`,ref:n?gn:void 0,onBlur:r?p=>{const j=p.relatedTarget;j instanceof Node&&p.currentTarget.contains(j)||We(!1)}:void 0,children:[r?t.jsxs("div",{className:s.sessionCardBody,children:[t.jsxs("div",{className:s.sessionMeta,children:[t.jsx("label",{className:"tf-field-label",htmlFor:"annotated-session-title",children:"Session title"}),t.jsx("input",{id:"annotated-session-title",className:`tf-field-shell ${s.textInput} ${s.sessionTitleInput}`,value:Re,onChange:p=>{Xe(p.target.value),Y(600)},placeholder:"Session title"}),t.jsx("span",{className:`tf-text-meta ${s.sessionTimestamp}`,children:Qt(e.updatedAt)})]}),St(e)?t.jsxs("div",{className:`tf-text-secondary ${s.sessionCreator}`,children:["Created by ",St(e)]}):null,t.jsxs("div",{className:s.sessionInstructionEditor,children:[t.jsx("label",{className:"tf-field-label",htmlFor:"annotated-session-instruction",children:"Session instruction"}),t.jsx("textarea",{id:"annotated-session-instruction",className:`tf-field-shell ${s.textArea} ${s.sessionInstructionField}`,value:Pe,onChange:p=>{Ve(p.target.value),Y(600)},placeholder:"Add overall instructions, context, or framing for this session."})]}),t.jsxs("div",{className:`tf-text-secondary ${s.annotationSummary}`,children:[e.annotations.length," annotation",e.annotations.length===1?"":"s"]})]}):t.jsxs("button",{type:"button",className:s.sessionCardButton,onClick:()=>{if(n){We(!0);return}H("session-switch").then(p=>{p&&($e(e.id),fe(e,d))})},"aria-pressed":n,children:[t.jsxs("div",{className:s.sessionMeta,children:[t.jsx("span",{className:`tf-heading-card ${s.sessionTitle}`,children:c}),t.jsx("span",{className:`tf-text-meta ${s.sessionTimestamp}`,children:Qt(e.updatedAt)})]}),St(e)?t.jsxs("div",{className:`tf-text-secondary ${s.sessionCreator}`,children:["Created by ",St(e)]}):null,i?t.jsx("div",{className:`tf-text-secondary ${s.sessionInstructionPreview}`,children:i}):null,t.jsxs("div",{className:`tf-text-secondary ${s.annotationSummary}`,children:[e.annotations.length," annotation",e.annotations.length===1?"":"s"]})]}),n?t.jsx("div",{className:s.sessionCardFooter,children:t.jsxs(t.Fragment,{children:[t.jsx("button",{type:"button",className:`tf-button-ghost ${s.toolRailButton} ${s.iconButton}`,onClick:()=>{ts()},disabled:W,"aria-label":"Duplicate session",title:`Duplicate session "${c}"`,children:t.jsx(Zt,{size:16})}),t.jsx("button",{type:"button",className:`tf-button-ghost ${s.toolRailButton} ${s.iconButton}`,onClick:()=>{rs()},disabled:W,"aria-label":"Delete session",title:`Delete session "${c}"`,children:t.jsx(Tn,{size:16})})]})}):null]},e.id)})}):t.jsxs("div",{className:s.emptyState,children:[t.jsx("strong",{className:"tf-empty-title",children:"No image selected"}),t.jsx("p",{className:"tf-empty-copy",children:"Open an image attachment from a task to start an annotated session."})]})]}),t.jsxs("section",{className:`tf-surface-panel ${s.panel} ${s.canvasPanel}`,children:[t.jsxs("div",{className:s.panelHeader,children:[t.jsxs("div",{className:s.canvasHeading,children:[t.jsx("div",{className:"tf-heading-card",children:d?.displayName||"Annotated attachment"}),t.jsx("div",{className:"tf-text-secondary",children:B?`${N.length} markers · ${gs}`:"Pick or create a session"})]}),at?t.jsx(Ts,{copied:pt,onClick:()=>{os()},title:"Copy image reference",ariaLabel:pt?"Copied image reference":"Copy image reference",label:at}):null]}),t.jsxs("div",{className:s.toolRail,children:[t.jsxs("div",{className:`${s.toolbarCluster} ${s.toolbarViewportCluster}`,children:[t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:()=>{ns()},disabled:!B||W||!xn&&U!=="error","aria-label":W?"Saving session":"Save session",title:W?"Saving session":"Save session",children:t.jsx(ks,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:()=>ht(!0),disabled:qe,"aria-label":"Open image",title:"Open image",children:t.jsx(ws,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:()=>{Jn()},disabled:gt,"aria-label":gt?"Pasting image":"Paste image",title:gt?"Pasting image":"Paste image",children:gt?t.jsx(An,{size:18,className:Bn.spin}):t.jsx(Cs,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:()=>{B&&qn()},disabled:!B||!xn,"aria-label":"Reset unsaved changes",title:"Reset unsaved changes",children:t.jsx(Is,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost ${s.toolRailButton} ${s.iconButton}`,onClick:ls,disabled:!d||ce||P<=.25,"aria-label":"Zoom out",children:t.jsx(Ss,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:cs,disabled:!d||ce||P===1,"aria-label":`Reset zoom to 100 percent (currently ${Yt})`,title:`Reset zoom to 100% (currently ${Yt})`,children:t.jsx("span",{children:Yt})}),t.jsx("button",{type:"button",className:`tf-button-ghost ${s.toolRailButton} ${s.iconButton}`,onClick:is,disabled:!d||ce||P>=4,"aria-label":"Zoom in",children:t.jsx(Ns,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.toolBtn} ${s.toolbarButton} ${G?s.toolBtnActive:""}`,onClick:()=>Qe(e=>!e),disabled:!d||ce||!ke,"aria-label":"Pan canvas",title:"Pan canvas",children:t.jsx($s,{size:18})})]}),t.jsx("span",{className:s.toolbarSeparator,"aria-hidden":"true"}),t.jsxs("div",{className:s.toolbarActions,children:[t.jsx("div",{className:s.toolbarSelectionActions,children:z?t.jsxs(t.Fragment,{children:[t.jsxs("div",{ref:bn,className:s.toolbarColorPicker,children:[t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton} ${s.colorPickerButton}`,onClick:()=>dt(e=>!e),"aria-label":"Marker color","aria-expanded":ct,title:"Marker color",children:t.jsx("span",{className:s.colorPickerSwatch,style:{backgroundColor:Ut},"aria-hidden":"true"})}),ct?t.jsx("div",{className:s.colorPickerPopover,role:"menu","aria-label":"Marker color options",children:Uo.map(e=>t.jsx("button",{type:"button",className:`${s.colorOption} ${Ut===e?s.colorOptionActive:""}`,style:{backgroundColor:e},onClick:()=>Qn(e),"aria-label":`Use marker color ${e}`,"aria-pressed":Ut===e},e))}):null]}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:Xt,"aria-label":"Delete marker",title:"Delete selected marker",children:t.jsx(Tn,{size:18})}),t.jsx("div",{className:s.toolbarGeometryFields,"aria-label":"Marker geometry percent controls",children:Qo(z).map(e=>t.jsxs("label",{className:s.toolbarGeometryField,children:[t.jsx("span",{className:`tf-text-meta ${s.toolbarGeometryLabel}`,children:e.label}),t.jsx("input",{className:`tf-field-shell ${s.toolbarGeometryInput}`,type:"number",min:0,max:100,step:.1,value:e.value,onChange:n=>{const r=Jo(n.target.value);r!==null&&ee(z.id,i=>Zo(i,e.key,r))},"aria-label":`${e.label} percent`})]},e.key))})]}):null}),t.jsxs("div",{className:s.toolbarUtilities,children:[t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.payloadBtn} ${s.toolbarButton}`,onClick:ss,disabled:!B||ft,"aria-label":ft?"Loading payload preview":"Preview payload",title:ft?"Loading payload preview":"Preview payload",children:t.jsx(tn,{size:18})}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:()=>dn(!0),"aria-label":"How to use marker tools",title:"How to use marker tools",children:t.jsx(nn,{size:18})})]})]})]}),t.jsxs("div",{className:s.canvasWorkspace,children:[t.jsx("div",{className:s.canvasToolRail,"aria-label":"Annotation tools",children:Mn.map(e=>{const n=e.icon;return t.jsx("button",{type:"button",className:`tf-button-ghost ${s.toolRailButton} ${$===e.value?s.toolBtnActive:""}`,onClick:()=>es(e.value),disabled:!d,title:e.label,"aria-label":`${e.label} tool. ${It[e.value].short}`,children:t.jsx(n,{size:18})},e.value)})}),t.jsx("div",{className:s.canvasMain,children:t.jsx("div",{ref:Te,className:`tf-scrollbar ${s.canvasScroller}`,children:d?t.jsxs(t.Fragment,{children:[ce&&!Lt?t.jsx("div",{className:s.canvasStatusOverlay,"aria-live":"polite",children:t.jsxs("div",{className:s.canvasStatusCard,children:[t.jsx(An,{size:20,className:Bn.spinner}),t.jsx("span",{children:"Loading image…"})]})}):null,Lt?t.jsx("div",{className:s.canvasStatusOverlay,"aria-live":"polite",children:t.jsx("div",{className:s.canvasStatusCard,children:t.jsx("span",{children:"Image failed to load."})})}):null,t.jsx("div",{className:s.canvasFrame,children:t.jsxs("div",{className:s.canvasMedia,style:{width:b.width?`${b.width}px`:void 0,height:b.height?`${b.height}px`:void 0},children:[t.jsx("img",{ref:Ht,src:d.path,alt:d.displayName,className:s.canvasImage,onLoad:()=>{const e=Ht.current;et({width:e?.naturalWidth||0,height:e?.naturalHeight||0}),Je(!1),Ze(!1)},onError:()=>{et({width:0,height:0}),Je(!1),Ze(!0)}}),!ce&&!Lt&&B?t.jsxs("div",{ref:mn,className:`${s.overlay} ${$==="select"?s.overlaySelect:""} ${G&&ke?s.overlayPan:""}`,onPointerDown:ds,onPointerMove:fs,onPointerUp:e=>{us(e),hs()},"data-testid":"annotated-attachment-overlay",onPointerLeave:ms,onPointerCancel:ps,children:[t.jsx("svg",{className:s.overlaySvg,viewBox:Gt,preserveAspectRatio:"none","aria-hidden":"true",children:N.filter(e=>e.kind==="arrow").map(e=>{const n=Ko(e,b),r=en(e),i=C===e.id;return t.jsxs(qt.Fragment,{children:[i?t.jsxs(t.Fragment,{children:[t.jsx("line",{x1:n.shaftX1,y1:n.shaftY1,x2:n.shaftX2,y2:n.shaftY2,stroke:"color-mix(in srgb, var(--surface-elevated) 92%, transparent)",strokeWidth:14,strokeLinecap:"round"}),t.jsx("polygon",{points:n.headPoints,fill:r,stroke:"color-mix(in srgb, var(--surface-elevated) 92%, transparent)",strokeWidth:4,strokeLinejoin:"round"})]}):null,t.jsx("line",{x1:n.shaftX1,y1:n.shaftY1,x2:n.shaftX2,y2:n.shaftY2,stroke:r,strokeWidth:i?10:6,strokeLinecap:"round"}),t.jsx("polygon",{points:n.headPoints,fill:r})]},e.id)})}),t.jsx("svg",{className:s.overlayHitLayer,viewBox:Gt,preserveAspectRatio:"none",children:N.filter(e=>e.kind==="arrow").map(e=>t.jsxs(qt.Fragment,{children:[t.jsx("line",{x1:e.x*b.width,y1:e.y*b.height,x2:e.x2*b.width,y2:e.y2*b.height,className:s.arrowHitArea,"data-testid":`annotated-arrow-hit-${e.id}`,"aria-label":`Arrow marker ${Kt.get(e.id)||0}`,onPointerDown:n=>Nn(n,e),onClick:n=>{n.stopPropagation(),L(e.id)}}),C===e.id?t.jsxs(t.Fragment,{children:[t.jsx("circle",{cx:e.x*b.width,cy:e.y*b.height,r:He.hitRadius,className:s.canvasHandleHit,role:"button",tabIndex:0,"aria-label":"Move arrow tail",onPointerDown:n=>$n(n,e,"tail")}),t.jsx("circle",{cx:e.x*b.width,cy:e.y*b.height,r:He.visibleRadius,className:s.canvasHandleVisible,"aria-hidden":"true"}),t.jsx("circle",{cx:e.x2*b.width,cy:e.y2*b.height,r:He.hitRadius,className:s.canvasHandleHit,role:"button",tabIndex:0,"aria-label":"Move arrow tip",onPointerDown:n=>$n(n,e,"tip")}),t.jsx("circle",{cx:e.x2*b.width,cy:e.y2*b.height,r:He.visibleRadius,className:s.canvasHandleVisible,"aria-hidden":"true"})]}):null]},`hit-${e.id}`))}),N.filter(e=>e.kind==="arrow").map(e=>t.jsx("div",{className:`${s.annotationNumberBadge} ${s.arrowNumberBadge}`,style:{left:`${(e.x+e.x2)/2*100}%`,top:`${(e.y+e.y2)/2*100}%`,backgroundColor:en(e)},"aria-hidden":"true",children:Kt.get(e.id)||0},`arrow-number-${e.id}`)),N.filter(e=>e.kind!=="arrow").map(e=>{const n=en(e),r=Kt.get(e.id)||0,i={onPointerDown:c=>Nn(c,e),onClick:c=>{c.stopPropagation(),L(e.id)}};return e.kind==="pin"?t.jsx("button",{type:"button",...i,className:`${s.pin} ${C===e.id?s.selected:""}`,style:{left:`${e.x*100}%`,top:`${e.y*100}%`,backgroundColor:n},children:r},e.id):e.kind==="text-note"?t.jsx("button",{type:"button",...i,className:`${s.note} ${C===e.id?s.selected:""}`,style:{left:`${e.x*100}%`,top:`${e.y*100}%`,backgroundColor:n},children:r},e.id):t.jsxs("div",{className:`${s.box} ${C===e.id?s.selected:""}`,style:{left:`${e.x*100}%`,top:`${e.y*100}%`,width:`${e.width*100}%`,height:`${e.height*100}%`,borderColor:n},children:[t.jsx("span",{className:`${s.annotationNumberBadge} ${s.boxNumberBadge}`,style:{backgroundColor:n},"aria-hidden":"true",children:r}),t.jsx("button",{type:"button",...i,className:s.boxSurface,"aria-label":`Box marker ${r}`})]},e.id)}),t.jsx("svg",{className:s.overlaySvg,viewBox:Gt,preserveAspectRatio:"none",children:N.filter(e=>e.kind==="box"&&C===e.id).map(e=>t.jsxs(qt.Fragment,{children:[t.jsx("circle",{cx:(e.x+e.width)*b.width,cy:(e.y+e.height)*b.height,r:He.hitRadius,className:`${s.canvasHandleHit} ${s.canvasResizeHandleHit}`,role:"button",tabIndex:0,"aria-label":"Resize box marker",onPointerDown:n=>bs(n,e)}),t.jsx("circle",{cx:(e.x+e.width)*b.width,cy:(e.y+e.height)*b.height,r:He.visibleRadius,className:s.canvasHandleVisible,"aria-hidden":"true"})]},`box-handle-${e.id}`))})]}):null]})})]}):t.jsx("div",{className:s.emptyState,children:t.jsx("p",{className:"tf-empty-copy",children:"Select an image attachment to annotate."})})})})]}),t.jsxs("div",{className:s.statusBar,children:[t.jsx("span",{children:U==="error"?"Save failed. Retry now.":U==="saving"||U==="pending"?"Saving…":"Saved"}),rn?t.jsx("span",{children:rn}):t.jsx("span",{children:B?G&&ke?"Drag on the image to pan.":$==="select"?"Select a marker to edit it.":`Click on the image to place a ${$}.`:"Create a session to begin placing markers on this image."}),d&&!B&&Kn?t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn} ${s.toolbarButton}`,onClick:()=>{wn()},disabled:W||ut,children:W?"Creating…":"Create one now"}):null]})]}),t.jsx("section",{className:`tf-surface-panel ${s.panel} ${s.detailPanel}`,children:B?t.jsxs(t.Fragment,{children:[t.jsxs("div",{className:s.panelHeader,children:[t.jsxs("div",{className:s.panelHeaderText,children:[t.jsx("div",{className:`tf-heading-card ${s.panelTitle}`,children:"Markers"}),t.jsxs("div",{className:"tf-text-secondary",children:[N.length," in this session"]})]}),t.jsxs("div",{className:s.sessionActions,children:[t.jsx("button",{type:"button",className:`tf-control-icon ${s.iconButton}`,onClick:()=>Sn("up"),disabled:!z||zt<=0,"aria-label":"Move marker up",children:t.jsx(Ps,{size:16})}),t.jsx("button",{type:"button",className:`tf-control-icon ${s.iconButton}`,onClick:()=>Sn("down"),disabled:!z||zt===-1||zt>=N.length-1,"aria-label":"Move marker down",children:t.jsx(Bs,{size:16})})]})]}),t.jsx("div",{className:`tf-scrollbar ${s.annotationList}`,children:N.length===0?t.jsx("div",{className:s.detailEmpty,children:t.jsx("p",{className:"tf-empty-copy",children:"Add a marker on the image to begin."})}):N.map((e,n)=>{const r=Oo[e.kind],i=Do[e.kind],c=`${i} ${n+1}`,p=zo[e.markerType||re],j=En.find(f=>f.value===(e.markerType||re))?.label||"Review",u=C===e.id;return t.jsxs("div",{className:`tf-surface-elevated ${s.annotationCard} ${u?s.annotationCardActive:""}`,children:[t.jsx("button",{type:"button",className:s.annotationCardButton,onClick:()=>L(e.id),"aria-label":i,children:t.jsxs("div",{className:s.annotationMeta,children:[t.jsx("span",{className:`tf-heading-card ${s.annotationTitle}`,children:c}),t.jsx("span",{className:`tf-text-meta ${s.annotationKind}`,"aria-hidden":"true",children:t.jsx(r,{size:16})})]})}),u?t.jsxs("div",{className:s.annotationInstructionEditor,children:[t.jsx("label",{className:"tf-field-label",htmlFor:"annotated-marker-instruction",children:"Marker instruction"}),t.jsx("textarea",{id:"annotated-marker-instruction",ref:u?pn:null,className:`tf-field-shell ${s.textArea} ${s.annotationInstructionField}`,value:e.instruction,onChange:f=>{ee(e.id,v=>({...v,instruction:f.target.value}),600)},onBlur:()=>{H("text")},placeholder:"What should the AI focus on for this marker?"}),t.jsxs("div",{className:s.annotationTypeField,children:[t.jsx("label",{className:"tf-field-label",htmlFor:"annotated-marker-type",children:"Instruction type"}),t.jsx("select",{id:"annotated-marker-type",className:`tf-field-shell ${s.select}`,value:e.markerType||re,onChange:f=>{ee(e.id,v=>Wo(v,f.target.value))},children:En.map(f=>t.jsx("option",{value:f.value,children:f.label},f.value))})]})]}):t.jsx("button",{type:"button",className:s.annotationInstructionButton,onClick:()=>L(e.id),"aria-label":`Edit ${i} instruction`,children:t.jsxs("div",{className:s.annotationInstructionEditor,children:[t.jsx("div",{className:`tf-text-secondary ${s.annotationInstructionPreview}`,children:e.instruction.trim()||"No marker instruction yet."}),t.jsx("div",{className:s.annotationPreviewFooter,children:t.jsx("span",{className:`tf-text-meta ${s.annotationInstructionTypeIcon}`,"aria-label":`Instruction type: ${j}`,title:j,children:t.jsx(p,{size:16})})})]})})]},e.id)})})]}):t.jsxs("div",{className:s.emptyState,children:[t.jsx("strong",{className:"tf-empty-title",children:"No active session"}),t.jsx("p",{className:"tf-empty-copy",children:"Create or select a session to edit annotations."})]})}),t.jsx(Jt,{isOpen:Vn,onClose:()=>{qe||(ht(!1),bt(""))},title:"Open Image",size:"sm",children:t.jsxs("form",{className:s.openImageModalBody,onSubmit:e=>{e.preventDefault(),Zn()},children:[t.jsx("label",{className:"tf-field-label",htmlFor:"annotated-open-image-reference",children:"Image reference number"}),t.jsx("input",{id:"annotated-open-image-reference",className:`tf-field-shell ${s.textInput} ${s.openImageField}`,value:Mt,onChange:e=>bt(e.target.value),placeholder:"I-24",autoFocus:!0}),t.jsxs("div",{className:s.openImageActions,children:[t.jsx("button",{type:"button",className:"tf-button-ghost tf-button-compact",onClick:()=>{ht(!1),bt("")},disabled:qe,children:"Cancel"}),t.jsx("button",{type:"submit",className:"tf-button-primary tf-button-compact",disabled:qe,children:qe?"Opening…":"Open"})]})]})}),t.jsx(Jt,{isOpen:Xn,onClose:()=>dn(!1),title:"How To Use Markers",size:"md",children:t.jsx("div",{className:s.markerHelpModalBody,children:Mn.filter(e=>e.value!=="select").map(e=>t.jsxs("div",{className:`tf-surface-inset ${s.markerHelpItem}`,children:[t.jsxs("div",{className:s.markerHelpHeader,children:[t.jsx("strong",{className:"tf-heading-card",children:e.label}),t.jsx("span",{className:"tf-text-meta",children:It[e.value].short})]}),t.jsx("p",{className:"tf-text-secondary",children:It[e.value].detail}),t.jsx("p",{className:`tf-text-body ${s.markerHelpExample}`,children:It[e.value].example})]},e.value))})}),t.jsx(Jt,{isOpen:Wn,onClose:()=>Bt(!1),title:"AI Payload",size:"lg",children:t.jsxs("div",{className:s.payloadModalBody,children:[t.jsxs("div",{className:s.payloadModalToolbar,children:[t.jsxs("div",{className:s.payloadViewToggle,children:[t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.toolBtn} ${Tt==="json"?s.toolBtnActive:""}`,onClick:()=>cn("json"),children:"JSON"}),t.jsx("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.toolBtn} ${Tt==="brief"?s.toolBtnActive:""}`,onClick:()=>cn("brief"),children:"AI Brief"})]}),t.jsxs("div",{className:s.payloadModalActions,children:[t.jsxs("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn}`,onClick:()=>{In("json")},disabled:!oe,"aria-label":le==="json"?"Copied JSON":"Copy JSON",title:le==="json"?"Copied JSON":"Copy JSON",children:[le==="json"?t.jsx(sn,{size:16}):t.jsx(Zt,{size:16}),t.jsx("span",{children:"JSON"})]}),t.jsxs("button",{type:"button",className:`tf-button-ghost tf-button-compact ${s.ghostBtn}`,onClick:()=>{In("brief")},disabled:!oe,"aria-label":le==="brief"?"Copied AI Brief":"Copy AI Brief",title:le==="brief"?"Copied AI Brief":"Copy AI Brief",children:[le==="brief"?t.jsx(sn,{size:16}):t.jsx(Zt,{size:16}),t.jsx("span",{children:"AI Brief"})]})]})]}),ft?t.jsx("div",{className:s.detailEmpty,children:t.jsx("p",{className:"tf-empty-copy",children:"Loading payload…"})}):oe?t.jsx("pre",{className:`tf-surface-inset tf-scrollbar ${s.payloadModalPreview}`,children:Tt==="json"?JSON.stringify(oe,null,2):Fn(oe)}):t.jsx("div",{className:s.detailEmpty,children:t.jsx("p",{className:"tf-empty-copy",children:"Load a payload preview to inspect the current session contract."})})]})})]})}export{dr as AnnotatedAttachmentWorkspaceShell};
|