@taskforcehq/taskforce 0.3.302 → 0.3.303

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/dist/Taskforce.module.css +11 -0
  2. package/dist/TaskforceCore.js +59 -9
  3. package/dist/TaskforceCore.test.js +717 -14
  4. package/dist/components/task/TaskKanban.test.js +17 -0
  5. package/dist/components/views/PlansPage.d.ts +3 -1
  6. package/dist/components/views/PlansPage.js +110 -51
  7. package/dist/components/views/PlansPage.test.d.ts +1 -0
  8. package/dist/components/views/PlansPage.test.js +100 -0
  9. package/dist/components/views/StandaloneLayout.d.ts +1 -0
  10. package/dist/components/views/StandaloneLayout.js +106 -151
  11. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +8 -3
  12. package/dist/components/views/standalone/modals/AccountHubModal.js +16 -2
  13. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
  14. package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
  15. package/dist/core/EntitlementsPolicy.test.js +2 -2
  16. package/dist/core/GlobalSettingsService.js +78 -18
  17. package/dist/core/PlanEntitlementService.d.ts +29 -5
  18. package/dist/core/PlanEntitlementService.js +297 -219
  19. package/dist/core/SignupMonetizationSettings.test.js +173 -49
  20. package/dist/core/SystemAdmin.test.js +186 -96
  21. package/dist/core/Taskforce.d.ts +51 -10
  22. package/dist/core/Taskforce.js +218 -49
  23. package/dist/core/Taskforce.mcpAuth.test.js +0 -2
  24. package/dist/core/UserProfileAvatarDrafts.test.js +12 -3
  25. package/dist/core/types.d.ts +11 -12
  26. package/dist/hooks/useSyncOrchestrator.js +17 -3
  27. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +97 -5
  28. package/dist/hooks/useTaskforce.d.ts +3 -0
  29. package/dist/hooks/useTaskforce.js +9 -4
  30. package/dist/mcp/runtime.js +90 -29
  31. package/dist/mcp/runtime.test.js +272 -81
  32. package/dist/migrations/billingSchemaParity.test.js +3 -0
  33. package/dist/migrations/taskSchemaMigrations.js +15 -0
  34. package/dist/server/index.cookieProxy.test.js +1 -0
  35. package/dist/server/index.js +2 -0
  36. package/dist/server/index.test.js +2 -1
  37. package/dist/server/mockStripe.d.ts +1 -0
  38. package/dist/server/mockStripe.js +2 -0
  39. package/dist/server/routes/admin.js +80 -23
  40. package/dist/server/routes/auth.d.ts +1 -0
  41. package/dist/server/routes/auth.js +194 -53
  42. package/dist/server/routes/billing.js +506 -23
  43. package/dist/server/routes/billing.test.js +937 -45
  44. package/dist/server/routes/shared.js +5 -3
  45. package/dist/server/routes/sync.integration.test.js +160 -0
  46. package/dist/server/routes/sync.js +15 -5
  47. package/dist/server/routes.js +3 -3
  48. package/dist/server/routes.test.js +287 -76
  49. package/dist/sync/cloudSyncApi.d.ts +1 -0
  50. package/dist/sync/contentSyncState.d.ts +10 -0
  51. package/dist/sync/contentSyncState.js +277 -16
  52. package/dist/sync/syncService.d.ts +1 -0
  53. package/dist/sync/syncService.js +21 -8
  54. package/dist/sync/workspacePullFeed.d.ts +10 -0
  55. package/dist/sync/workspacePullFeed.js +111 -63
  56. package/dist/sync/workspacePullFeed.test.js +112 -0
  57. package/dist/ui/assets/{AgentsModule-JG5jc3he.js → AgentsModule-CpsTmrIW.js} +1 -1
  58. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dvi02Y7q.js → AnnotatedAttachmentWorkspace-C_TmXZwA.js} +1 -1
  59. package/dist/ui/assets/{DocumentWorkspace-iys3zfca.js → DocumentWorkspace-C3GyzrWm.js} +1 -1
  60. package/dist/ui/assets/{InitiativesModule-DJvLJI3A.js → InitiativesModule-4-Rh2K2n.js} +1 -1
  61. package/dist/ui/assets/PlansPage-BP7AYOqr.js +1 -0
  62. package/dist/ui/assets/{TaskSettings-Bqd4cCzW.js → TaskSettings-BOC5F6Ag.js} +1 -1
  63. package/dist/ui/assets/{WorkflowsModule-BguHeQaV.js → WorkflowsModule-CGk9s3NJ.js} +1 -1
  64. package/dist/ui/assets/index-CLIMgR6g.js +6 -0
  65. package/dist/ui/assets/index-DneETxcu.css +1 -0
  66. package/dist/ui/assets/{vendor-icons-C8EED3Oh.js → vendor-icons-pWocEipT.js} +1 -1
  67. package/dist/ui/index.html +3 -3
  68. package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
  69. package/dist/utils/accountProfileSummaryCache.js +97 -0
  70. package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
  71. package/dist/utils/accountProfileSummaryCache.test.js +63 -0
  72. package/package.json +3 -3
  73. package/dist/ui/assets/PlansPage-BMPlpFpw.js +0 -1
  74. package/dist/ui/assets/index-MXeWoebC.css +0 -1
  75. package/dist/ui/assets/index-YJ4k5QNa.js +0 -6
  76. package/dist/utils/billingStatusCache.d.ts +0 -21
  77. package/dist/utils/billingStatusCache.js +0 -70
@@ -1,5 +1,6 @@
1
1
  import { serializeTaskForSync } from './taskSyncPayload.js';
2
2
  export async function buildWorkspacePullFeed(input) {
3
+ const buildStartedAtMs = Date.now();
3
4
  const { core, workspaceId, cursor, pageSize, encodeWorkspaceSyncCursor, contentSyncState, collaborationSyncState } = input;
4
5
  const syncCursor = cursor ? { watermark: cursor.u, id: cursor.id } : null;
5
6
  const dbFetchLimit = pageSize * 2 + 10;
@@ -62,13 +63,14 @@ export async function buildWorkspacePullFeed(input) {
62
63
  profile
63
64
  }))
64
65
  .filter((entry) => Number.isFinite(Date.parse(entry.watermark)) && entry.sortId.length > 0);
65
- const documentEntries = (await contentSyncState.listWorkspaceSyncDocumentsSnapshot(core.getPaths().basePath, workspaceId))
66
+ const indexStartedAtMs = Date.now();
67
+ const documentIndex = await contentSyncState.listWorkspaceSyncDocumentIndex(core.getPaths().basePath, workspaceId);
68
+ const documentEntries = documentIndex
66
69
  .map((doc) => ({
67
70
  kind: 'document',
68
71
  watermark: String(doc.updatedAt || '').trim(),
69
72
  sortId: `doc:${String(doc.path || '').trim()}`,
70
73
  path: String(doc.path || '').trim(),
71
- content: String(doc.content || ''),
72
74
  assetId: doc.assetId,
73
75
  documentId: doc.documentId,
74
76
  referenceNumber: doc.referenceNumber,
@@ -146,13 +148,13 @@ export async function buildWorkspacePullFeed(input) {
146
148
  sessionId: String(session.id || '').trim()
147
149
  }))
148
150
  .filter((entry) => Number.isFinite(Date.parse(entry.watermark)) && entry.sessionId.length > 0);
149
- const assetEntries = (await contentSyncState.listWorkspaceSyncBinaryAssetsSnapshot(core.getPaths().basePath, workspaceId))
151
+ const assetIndex = await contentSyncState.listWorkspaceSyncBinaryAssetIndex(workspaceId);
152
+ const assetEntries = assetIndex
150
153
  .map((asset) => ({
151
154
  kind: 'asset',
152
155
  watermark: String(asset.updatedAt || '').trim(),
153
156
  sortId: `asset:${String(asset.path || '').trim()}`,
154
157
  path: String(asset.path || '').trim(),
155
- contentBase64: String(asset.contentBase64 || ''),
156
158
  assetId: asset.assetId,
157
159
  kindValue: asset.kind,
158
160
  mimeType: asset.mimeType,
@@ -163,7 +165,7 @@ export async function buildWorkspacePullFeed(input) {
163
165
  originalFilename: asset.originalFilename,
164
166
  linkRole: asset.linkRole
165
167
  }))
166
- .filter((entry) => Number.isFinite(Date.parse(entry.watermark)) && entry.path.length > 0 && entry.contentBase64.length > 0);
168
+ .filter((entry) => Number.isFinite(Date.parse(entry.watermark)) && entry.path.length > 0);
167
169
  const assetDeleteEntries = contentSyncState.listWorkspaceSyncBinaryAssetDeletesSnapshot(workspaceId)
168
170
  .map((asset) => ({
169
171
  kind: 'asset-delete',
@@ -206,11 +208,6 @@ export async function buildWorkspacePullFeed(input) {
206
208
  return String(a.sortId || '').localeCompare(String(b.sortId || ''));
207
209
  });
208
210
  const page = afterCursor.slice(0, pageSize);
209
- const hasMore = afterCursor.length > pageSize;
210
- const last = page.length > 0 ? page[page.length - 1] : null;
211
- const nextCursor = encodeWorkspaceSyncCursor(last
212
- ? { u: last.watermark, id: String(last.sortId || '') }
213
- : cursor);
214
211
  const workspaceMembers = core.listWorkspaceUsers(workspaceId).map((member) => ({
215
212
  userId: member.userId,
216
213
  workspaceId,
@@ -225,77 +222,128 @@ export async function buildWorkspacePullFeed(input) {
225
222
  updatedAt: member.updatedAt || null,
226
223
  emailVerifiedAt: member.emailVerifiedAt || null
227
224
  }));
228
- const changes = page.map((entry) => (entry.kind === 'delete'
229
- ? { op: 'delete', taskId: entry.taskId, deletedAt: entry.watermark, watermark: entry.watermark }
225
+ const indexMs = Date.now() - indexStartedAtMs;
226
+ const hydratedDocuments = new Map();
227
+ const hydratedAssets = new Map();
228
+ const hydrationStartedAtMs = Date.now();
229
+ await Promise.all(page.map(async (entry) => {
230
+ if (entry.kind === 'document') {
231
+ const payload = await contentSyncState.loadWorkspaceSyncDocumentPayload(core.getPaths().basePath, workspaceId, entry.path);
232
+ hydratedDocuments.set(entry.path, payload);
233
+ return;
234
+ }
235
+ if (entry.kind === 'asset') {
236
+ const payload = await contentSyncState.loadWorkspaceSyncBinaryAssetPayload(core.getPaths().basePath, workspaceId, entry.path);
237
+ hydratedAssets.set(entry.path, payload);
238
+ }
239
+ }));
240
+ const hydrationMs = Date.now() - hydrationStartedAtMs;
241
+ const effectivePage = [];
242
+ for (const entry of page) {
243
+ if (entry.kind === 'document' && !hydratedDocuments.get(entry.path))
244
+ break;
245
+ if (entry.kind === 'asset' && !hydratedAssets.get(entry.path))
246
+ break;
247
+ effectivePage.push(entry);
248
+ }
249
+ const hasMore = afterCursor.length > effectivePage.length;
250
+ const last = effectivePage.length > 0 ? effectivePage[effectivePage.length - 1] : null;
251
+ const nextCursor = encodeWorkspaceSyncCursor(last
252
+ ? { u: last.watermark, id: String(last.sortId || '') }
253
+ : cursor);
254
+ const encodeStartedAtMs = Date.now();
255
+ const changes = effectivePage.flatMap((entry) => (entry.kind === 'delete'
256
+ ? [{ op: 'delete', taskId: entry.taskId, watermark: entry.watermark }]
230
257
  : entry.kind === 'taxonomy'
231
- ? {
232
- op: 'taxonomy-upsert',
233
- taxonomies: entry.taxonomies,
234
- ...(entry.taxonomyState ? { taxonomyState: entry.taxonomyState } : {}),
235
- updatedAt: entry.watermark,
236
- watermark: entry.watermark
237
- }
258
+ ? [{
259
+ op: 'taxonomy-upsert',
260
+ taxonomies: entry.taxonomies,
261
+ ...(entry.taxonomyState ? { taxonomyState: entry.taxonomyState } : {}),
262
+ updatedAt: entry.watermark,
263
+ watermark: entry.watermark
264
+ }]
238
265
  : entry.kind === 'initiative'
239
- ? { op: 'initiative-upsert', initiative: entry.initiative, updatedAt: entry.watermark, watermark: entry.watermark }
266
+ ? [{ op: 'initiative-upsert', initiative: entry.initiative, watermark: entry.watermark }]
240
267
  : entry.kind === 'workstream'
241
- ? { op: 'workstream-upsert', workstream: entry.workstream, updatedAt: entry.watermark, watermark: entry.watermark }
268
+ ? [{ op: 'workstream-upsert', workstream: entry.workstream, watermark: entry.watermark }]
242
269
  : entry.kind === 'ai-profile'
243
- ? { op: 'ai-profile-upsert', profile: entry.profile, updatedAt: entry.watermark, watermark: entry.watermark }
270
+ ? [{ op: 'ai-profile-upsert', profile: entry.profile, watermark: entry.watermark }]
244
271
  : entry.kind === 'document-review-session'
245
- ? { op: 'document-review-session-upsert', session: entry.session, updatedAt: entry.watermark, watermark: entry.watermark }
272
+ ? [{ op: 'document-review-session-upsert', session: entry.session, watermark: entry.watermark }]
246
273
  : entry.kind === 'document-review-session-delete'
247
- ? { op: 'document-review-session-delete', sessionId: entry.sessionId, deletedAt: entry.watermark, watermark: entry.watermark }
274
+ ? [{ op: 'document-review-session-delete', sessionId: entry.sessionId, deletedAt: entry.watermark, watermark: entry.watermark }]
248
275
  : entry.kind === 'document-review-comment'
249
- ? { op: 'document-review-comment-upsert', comment: entry.comment, updatedAt: entry.watermark, watermark: entry.watermark }
276
+ ? [{ op: 'document-review-comment-upsert', comment: entry.comment, watermark: entry.watermark }]
250
277
  : entry.kind === 'document-review-comment-delete'
251
- ? { op: 'document-review-comment-delete', commentId: entry.commentId, deletedAt: entry.watermark, watermark: entry.watermark }
278
+ ? [{ op: 'document-review-comment-delete', commentId: entry.commentId, deletedAt: entry.watermark, watermark: entry.watermark }]
252
279
  : entry.kind === 'annotated-attachment-session'
253
- ? { op: 'annotated-attachment-session-upsert', session: entry.session, updatedAt: entry.watermark, watermark: entry.watermark }
280
+ ? [{ op: 'annotated-attachment-session-upsert', session: entry.session, watermark: entry.watermark }]
254
281
  : entry.kind === 'annotated-attachment-session-delete'
255
- ? { op: 'annotated-attachment-session-delete', sessionId: entry.sessionId, deletedAt: entry.watermark, watermark: entry.watermark }
282
+ ? [{ op: 'annotated-attachment-session-delete', sessionId: entry.sessionId, deletedAt: entry.watermark, watermark: entry.watermark }]
256
283
  : entry.kind === 'document'
257
- ? {
258
- op: 'document-upsert',
259
- path: entry.path,
260
- updatedAt: entry.watermark,
261
- content: entry.content,
262
- assetId: entry.assetId,
263
- documentId: entry.documentId,
264
- referenceNumber: entry.referenceNumber,
265
- version: entry.version,
266
- taskId: entry.taskId,
267
- logicalName: entry.logicalName,
268
- caption: entry.caption,
269
- originalFilename: entry.originalFilename,
270
- linkRole: entry.linkRole,
271
- watermark: entry.watermark
272
- }
273
- : entry.kind === 'document-delete'
274
- ? { op: 'document-delete', path: entry.path, assetId: entry.assetId, deletedAt: entry.watermark, watermark: entry.watermark }
275
- : entry.kind === 'asset'
276
- ? {
277
- op: 'asset-upsert',
284
+ ? (() => {
285
+ const payload = hydratedDocuments.get(entry.path);
286
+ if (!payload)
287
+ return [];
288
+ return [{
289
+ op: 'document-upsert',
278
290
  path: entry.path,
279
291
  updatedAt: entry.watermark,
280
- contentBase64: entry.contentBase64,
281
- assetId: entry.assetId,
282
- kind: entry.kindValue,
283
- mimeType: entry.mimeType,
284
- referenceNumber: entry.referenceNumber,
285
- taskId: entry.taskId,
286
- logicalName: entry.logicalName,
287
- caption: entry.caption,
288
- originalFilename: entry.originalFilename,
289
- linkRole: entry.linkRole,
292
+ content: payload.content,
293
+ assetId: payload.assetId,
294
+ documentId: payload.documentId,
295
+ referenceNumber: payload.referenceNumber,
296
+ version: payload.version,
297
+ taskId: payload.taskId,
298
+ logicalName: payload.logicalName,
299
+ caption: payload.caption,
300
+ originalFilename: payload.originalFilename,
301
+ linkRole: payload.linkRole === 'reference' ? 'reference' : 'attachment',
290
302
  watermark: entry.watermark
291
- }
303
+ }];
304
+ })()
305
+ : entry.kind === 'document-delete'
306
+ ? [{ op: 'document-delete', path: entry.path, assetId: entry.assetId, deletedAt: entry.watermark, watermark: entry.watermark }]
307
+ : entry.kind === 'asset'
308
+ ? (() => {
309
+ const payload = hydratedAssets.get(entry.path);
310
+ if (!payload)
311
+ return [];
312
+ return [{
313
+ op: 'asset-upsert',
314
+ path: entry.path,
315
+ updatedAt: entry.watermark,
316
+ contentBase64: payload.contentBase64,
317
+ assetId: payload.assetId,
318
+ kind: payload.kind,
319
+ mimeType: payload.mimeType,
320
+ referenceNumber: payload.referenceNumber,
321
+ taskId: payload.taskId,
322
+ logicalName: payload.logicalName,
323
+ caption: payload.caption,
324
+ originalFilename: payload.originalFilename,
325
+ linkRole: payload.linkRole,
326
+ watermark: entry.watermark
327
+ }];
328
+ })()
292
329
  : entry.kind === 'asset-delete'
293
- ? { op: 'asset-delete', path: entry.path, assetId: entry.assetId, deletedAt: entry.watermark, watermark: entry.watermark }
294
- : { op: 'upsert', task: entry.task, archived: Boolean(entry.archived), watermark: entry.watermark }));
330
+ ? [{ op: 'asset-delete', path: entry.path, assetId: entry.assetId, deletedAt: entry.watermark, watermark: entry.watermark }]
331
+ : [{ op: 'upsert', task: entry.task, archived: Boolean(entry.archived), watermark: entry.watermark }]));
332
+ const encodeMs = Date.now() - encodeStartedAtMs;
295
333
  return {
296
334
  workspaceMembers,
297
335
  changes,
298
336
  nextCursor,
299
- hasMore
337
+ hasMore,
338
+ diagnostics: {
339
+ totalEntries: all.length,
340
+ pageEntries: page.length,
341
+ documentIndexCount: documentIndex.length,
342
+ assetIndexCount: assetIndex.length,
343
+ indexMs,
344
+ hydrationMs,
345
+ encodeMs,
346
+ buildMs: Date.now() - buildStartedAtMs
347
+ }
300
348
  };
301
349
  }
@@ -14,9 +14,13 @@ function createCoreMock() {
14
14
  }
15
15
  function createContentSyncStateMock() {
16
16
  return {
17
+ listWorkspaceSyncDocumentIndex: async () => [],
17
18
  listWorkspaceSyncDocumentsSnapshot: async () => [],
19
+ loadWorkspaceSyncDocumentPayload: async () => null,
18
20
  listWorkspaceSyncDocumentDeletesSnapshot: () => [],
21
+ listWorkspaceSyncBinaryAssetIndex: async () => [],
19
22
  listWorkspaceSyncBinaryAssetsSnapshot: async () => [],
23
+ loadWorkspaceSyncBinaryAssetPayload: async () => null,
20
24
  listWorkspaceSyncBinaryAssetDeletesSnapshot: () => [],
21
25
  };
22
26
  }
@@ -163,4 +167,112 @@ describe('workspacePullFeed', () => {
163
167
  })
164
168
  ]);
165
169
  });
170
+ it('hydrates document and asset payloads only for entries included in the current page', async () => {
171
+ const core = createCoreMock();
172
+ const content = createContentSyncStateMock();
173
+ const loadedDocs = [];
174
+ const loadedAssets = [];
175
+ content.listWorkspaceSyncDocumentIndex = async () => [
176
+ {
177
+ path: 'docs/a.md',
178
+ updatedAt: '2026-04-10T10:00:00.000Z',
179
+ assetId: 'asset-doc-a',
180
+ },
181
+ {
182
+ path: 'docs/z.md',
183
+ updatedAt: '2026-04-12T10:00:00.000Z',
184
+ assetId: 'asset-doc-z',
185
+ }
186
+ ];
187
+ content.loadWorkspaceSyncDocumentPayload = async (_basePath, _workspaceId, relativePath) => {
188
+ loadedDocs.push(relativePath);
189
+ return {
190
+ path: relativePath,
191
+ updatedAt: relativePath === 'docs/a.md' ? '2026-04-10T10:00:00.000Z' : '2026-04-12T10:00:00.000Z',
192
+ content: `content:${relativePath}`
193
+ };
194
+ };
195
+ content.listWorkspaceSyncBinaryAssetIndex = async () => [
196
+ {
197
+ path: 'workspaces/ws/assets/images/a.png',
198
+ updatedAt: '2026-04-11T10:00:00.000Z',
199
+ assetId: 'asset-image-a',
200
+ kind: 'image',
201
+ mimeType: 'image/png'
202
+ }
203
+ ];
204
+ content.loadWorkspaceSyncBinaryAssetPayload = async (_basePath, _workspaceId, assetPath) => {
205
+ loadedAssets.push(assetPath);
206
+ return {
207
+ path: assetPath,
208
+ updatedAt: '2026-04-11T10:00:00.000Z',
209
+ contentBase64: 'Zm9v',
210
+ kind: 'image',
211
+ mimeType: 'image/png'
212
+ };
213
+ };
214
+ const result = await buildWorkspacePullFeed({
215
+ core,
216
+ workspaceId: 'workspace-3',
217
+ cursor: null,
218
+ pageSize: 2,
219
+ encodeWorkspaceSyncCursor: encodeCursor,
220
+ contentSyncState: content,
221
+ collaborationSyncState: createCollaborationSyncStateMock(),
222
+ });
223
+ expect(result.changes).toHaveLength(2);
224
+ expect(result.changes[0]).toEqual(expect.objectContaining({
225
+ op: 'document-upsert',
226
+ path: 'docs/a.md',
227
+ content: 'content:docs/a.md'
228
+ }));
229
+ expect(result.changes[1]).toEqual(expect.objectContaining({
230
+ op: 'asset-upsert',
231
+ path: 'workspaces/ws/assets/images/a.png',
232
+ contentBase64: 'Zm9v'
233
+ }));
234
+ expect(loadedDocs).toEqual(['docs/a.md']);
235
+ expect(loadedAssets).toEqual(['workspaces/ws/assets/images/a.png']);
236
+ });
237
+ it('does not advance the cursor past a hydration miss', async () => {
238
+ const core = createCoreMock();
239
+ const content = createContentSyncStateMock();
240
+ content.listWorkspaceSyncDocumentIndex = async () => [
241
+ {
242
+ path: 'docs/a.md',
243
+ updatedAt: '2026-04-10T10:00:00.000Z',
244
+ assetId: 'asset-doc-a',
245
+ },
246
+ {
247
+ path: 'docs/b.md',
248
+ updatedAt: '2026-04-11T10:00:00.000Z',
249
+ assetId: 'asset-doc-b',
250
+ }
251
+ ];
252
+ content.loadWorkspaceSyncDocumentPayload = async (_basePath, _workspaceId, relativePath) => (relativePath === 'docs/a.md'
253
+ ? {
254
+ path: relativePath,
255
+ updatedAt: '2026-04-10T10:00:00.000Z',
256
+ content: 'content:docs/a.md'
257
+ }
258
+ : null);
259
+ const result = await buildWorkspacePullFeed({
260
+ core,
261
+ workspaceId: 'workspace-4',
262
+ cursor: null,
263
+ pageSize: 2,
264
+ encodeWorkspaceSyncCursor: encodeCursor,
265
+ contentSyncState: content,
266
+ collaborationSyncState: createCollaborationSyncStateMock(),
267
+ });
268
+ expect(result.changes).toEqual([
269
+ expect.objectContaining({
270
+ op: 'document-upsert',
271
+ path: 'docs/a.md',
272
+ content: 'content:docs/a.md'
273
+ })
274
+ ]);
275
+ expect(result.nextCursor).toBe('2026-04-10T10:00:00.000Z|doc:docs/a.md');
276
+ expect(result.hasMore).toBe(true);
277
+ });
166
278
  });
@@ -1 +1 @@
1
- import{r as n,j as e}from"./vendor-react-CKJs5o3c.js";import{p as T,u as C,v as w,w as S,t as s,x as M}from"./index-YJ4k5QNa.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-C8EED3Oh.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
+ import{r as n,j as e}from"./vendor-react-CKJs5o3c.js";import{p as T,u as C,v as w,w as S,t as s,x as M}from"./index-CLIMgR6g.js";import{h as L,B as A,k as I,a9 as H,aH as B,aI as D,b as R}from"./vendor-icons-pWocEipT.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-YJ4k5QNa.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-C8EED3Oh.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-CLIMgR6g.js";import{q as _s,m as js,c as Zt,T as Tn,s as ks,a2 as ws,h as An,aj as Cs,R as Is,ak as Ss,al as Ns,am as $s,an as tn,C as nn,ao as Rs,ap as Dn,aq as zn,ar as Un,as as Gn,g as Ps,f as Bs,b as sn}from"./vendor-icons-pWocEipT.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};