@adoptai/genui-components 0.1.57 → 0.1.58

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 (37) hide show
  1. package/dist/composites/document-field-extraction/FieldAuditLog.d.ts +27 -0
  2. package/dist/composites/document-field-extraction/FieldAuditLog.d.ts.map +1 -0
  3. package/dist/composites/document-field-extraction/FieldDetail.d.ts +22 -0
  4. package/dist/composites/document-field-extraction/FieldDetail.d.ts.map +1 -0
  5. package/dist/composites/document-field-extraction/FieldDetails.d.ts +18 -0
  6. package/dist/composites/document-field-extraction/FieldDetails.d.ts.map +1 -0
  7. package/dist/composites/document-field-extraction/bboxTransform.d.ts +25 -0
  8. package/dist/composites/document-field-extraction/bboxTransform.d.ts.map +1 -0
  9. package/dist/composites/document-field-extraction/fieldLabels.d.ts +2 -0
  10. package/dist/composites/document-field-extraction/fieldLabels.d.ts.map +1 -0
  11. package/dist/composites/document-field-extraction/resolver.d.ts +16 -0
  12. package/dist/composites/document-field-extraction/resolver.d.ts.map +1 -0
  13. package/dist/index.cjs +2097 -246
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +1892 -45
  18. package/dist/index.js.map +1 -1
  19. package/dist/renderer.cjs +1917 -181
  20. package/dist/renderer.cjs.map +1 -1
  21. package/dist/renderer.js +1774 -38
  22. package/dist/renderer.js.map +1 -1
  23. package/dist/resolver.cjs +1916 -180
  24. package/dist/resolver.cjs.map +1 -1
  25. package/dist/resolver.d.ts.map +1 -1
  26. package/dist/resolver.js +1774 -38
  27. package/dist/resolver.js.map +1 -1
  28. package/dist/schemas/document-field-extraction.d.ts +258 -0
  29. package/dist/schemas/document-field-extraction.d.ts.map +1 -0
  30. package/dist/schemas/index.cjs +178 -0
  31. package/dist/schemas/index.cjs.map +1 -1
  32. package/dist/schemas/index.d.ts +258 -0
  33. package/dist/schemas/index.d.ts.map +1 -1
  34. package/dist/schemas/index.js +178 -0
  35. package/dist/schemas/index.js.map +1 -1
  36. package/dist/tool-definitions.json +227 -3
  37. package/package.json +1 -1
package/dist/resolver.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import React41, { lazy, createContext, useState, useCallback, useEffect, useRef, useMemo, useContext, Suspense, useLayoutEffect } from 'react';
2
+ import React45, { lazy, createContext, useState, useCallback, useEffect, useRef, useMemo, useContext, Suspense, useLayoutEffect } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import dagre from '@dagrejs/dagre';
5
5
  import { createPortal } from 'react-dom';
@@ -5277,6 +5277,183 @@ var workflowStepperTool = {
5277
5277
  required: ["type", "title", "steps"]
5278
5278
  }
5279
5279
  };
5280
+ var bbox = z.tuple([z.number(), z.number(), z.number(), z.number()]);
5281
+ var fieldSchema = z.object({
5282
+ field_key: z.string(),
5283
+ label: z.string(),
5284
+ value: z.string(),
5285
+ page: z.number(),
5286
+ // 0-indexed, matches PageDimension.page
5287
+ bbox: bbox.nullable().optional(),
5288
+ confidence: z.number().optional(),
5289
+ confidence_tier: z.enum(["green", "amber", "red"]).optional(),
5290
+ badge: z.enum(["SC", "CN", "AB", "SW"]).optional(),
5291
+ state: z.enum(["ok", "corrected", "missing", "warn"]).optional(),
5292
+ entity_type: z.string().optional(),
5293
+ is_primary: z.boolean().optional(),
5294
+ // Set after a human correction (extractor output, or an optimistic in-card
5295
+ // edit) so the editable field card can show old → new and who changed it.
5296
+ previous_value: z.string().nullable().optional(),
5297
+ corrected_by: z.string().nullable().optional()
5298
+ });
5299
+ var pageSchema = z.object({
5300
+ page: z.number(),
5301
+ // 0-indexed
5302
+ imageUrl: z.string().optional(),
5303
+ // host-resolved from the durable image_ref
5304
+ width_pt: z.number(),
5305
+ height_pt: z.number()
5306
+ });
5307
+ var summaryStatSchema = z.object({
5308
+ field_key: z.string(),
5309
+ label: z.string(),
5310
+ value: z.string(),
5311
+ caption: z.string().nullable().optional()
5312
+ });
5313
+ var summaryFactSchema = z.object({
5314
+ field_key: z.string(),
5315
+ label: z.string(),
5316
+ value: z.string(),
5317
+ badge: z.string().nullable().optional()
5318
+ });
5319
+ var summarySchema = z.object({
5320
+ display_name: z.string().nullable().optional(),
5321
+ short_name: z.string().nullable().optional(),
5322
+ organization: z.string().nullable().optional(),
5323
+ domain: z.string().nullable().optional(),
5324
+ domain_group: z.string().nullable().optional(),
5325
+ tax_year: z.union([z.string(), z.number()]).nullable().optional(),
5326
+ crumbs: z.array(z.string()).optional(),
5327
+ tags: z.array(z.string()).optional(),
5328
+ stats: z.array(summaryStatSchema).optional(),
5329
+ facts: z.array(summaryFactSchema).optional()
5330
+ });
5331
+ var documentFieldExtractionSchema = z.object({
5332
+ type: z.literal("document-field-extraction"),
5333
+ doc_type: z.string().optional(),
5334
+ // Original uploaded filename (e.g. "2024_gl.pdf"); preferred over doc_type for
5335
+ // the card title since doc_type is often the "generic_fallback" schema name.
5336
+ file_name: z.string().optional(),
5337
+ // Server-side token; resolved by the host into pages/fields before render.
5338
+ payload_ref: z.string().optional(),
5339
+ // Present for persisted workstream docs; absent for pre-existing cards.
5340
+ store_id: z.string().optional(),
5341
+ doc_id: z.string().optional(),
5342
+ pages: z.array(pageSchema).max(100).optional(),
5343
+ fields: z.array(fieldSchema).max(1e3).optional(),
5344
+ // Page-count metadata for the Details tab ("N of M pages extracted").
5345
+ page_count: z.number().optional(),
5346
+ pages_processed: z.number().optional(),
5347
+ // Deterministic document summary for the Details tab.
5348
+ summary: summarySchema.nullable().optional()
5349
+ });
5350
+ var documentFieldExtractionTool = {
5351
+ name: "render_document_field_extraction",
5352
+ description: "Render extracted document fields as an interactive overlay on the page image(s) \u2014 colored bounding boxes per field plus a synced field-card list. Use after the extract_document tool returns; pass the returned payload_ref (the host expands it). For invoices, statements, tax forms, receipts, KYC/loan docs, POs where the user wants to SEE where each value sits on the page.",
5353
+ input_schema: {
5354
+ type: "object",
5355
+ properties: {
5356
+ type: { type: "string", enum: ["document-field-extraction"] },
5357
+ doc_type: { type: "string" },
5358
+ file_name: {
5359
+ type: "string",
5360
+ description: "Original uploaded filename; shown as the card title."
5361
+ },
5362
+ payload_ref: {
5363
+ type: "string",
5364
+ description: "Durable store key returned by extract_document; the GenUI host expands it into pages/fields at render time. Pass this verbatim \u2014 do NOT hand-build pages/fields."
5365
+ },
5366
+ store_id: {
5367
+ type: "string",
5368
+ description: "Docstore store id; present for persisted workstream docs. The host uses this to open the editable viewer."
5369
+ },
5370
+ doc_id: {
5371
+ type: "string",
5372
+ description: "Docstore document id; present for persisted workstream docs. Required for the editable viewer."
5373
+ },
5374
+ pages: {
5375
+ type: "array",
5376
+ items: {
5377
+ type: "object",
5378
+ properties: {
5379
+ page: { type: "number", description: "0-indexed page number" },
5380
+ imageUrl: { type: "string" },
5381
+ width_pt: { type: "number" },
5382
+ height_pt: { type: "number" }
5383
+ },
5384
+ required: ["page", "width_pt", "height_pt"]
5385
+ }
5386
+ },
5387
+ fields: {
5388
+ type: "array",
5389
+ items: {
5390
+ type: "object",
5391
+ properties: {
5392
+ field_key: { type: "string" },
5393
+ label: { type: "string" },
5394
+ value: { type: "string" },
5395
+ page: { type: "number", description: "0-indexed page number" },
5396
+ bbox: {
5397
+ type: "array",
5398
+ items: { type: "number" },
5399
+ description: "[x0,y0,x1,y1] PDF points, origin bottom-left; null if unlocated"
5400
+ },
5401
+ confidence: { type: "number" },
5402
+ confidence_tier: { type: "string", enum: ["green", "amber", "red"] },
5403
+ badge: { type: "string", enum: ["SC", "CN", "AB", "SW"] },
5404
+ state: { type: "string", enum: ["ok", "corrected", "missing", "warn"] },
5405
+ entity_type: { type: "string" },
5406
+ is_primary: { type: "boolean" }
5407
+ },
5408
+ required: ["field_key", "label", "value", "page"]
5409
+ }
5410
+ },
5411
+ page_count: { type: "number" },
5412
+ pages_processed: { type: "number" },
5413
+ summary: {
5414
+ type: "object",
5415
+ description: "Deterministic document summary for the Details tab (display_name, organization, domain, stats, facts, tags). Produced by the extractor \u2014 pass through verbatim.",
5416
+ properties: {
5417
+ display_name: { type: "string" },
5418
+ short_name: { type: "string" },
5419
+ organization: { type: "string" },
5420
+ domain: { type: "string" },
5421
+ domain_group: { type: "string" },
5422
+ tax_year: { type: "string" },
5423
+ crumbs: { type: "array", items: { type: "string" } },
5424
+ tags: { type: "array", items: { type: "string" } },
5425
+ stats: {
5426
+ type: "array",
5427
+ items: {
5428
+ type: "object",
5429
+ properties: {
5430
+ field_key: { type: "string" },
5431
+ label: { type: "string" },
5432
+ value: { type: "string" },
5433
+ caption: { type: "string" }
5434
+ },
5435
+ required: ["field_key", "label", "value"]
5436
+ }
5437
+ },
5438
+ facts: {
5439
+ type: "array",
5440
+ items: {
5441
+ type: "object",
5442
+ properties: {
5443
+ field_key: { type: "string" },
5444
+ label: { type: "string" },
5445
+ value: { type: "string" },
5446
+ badge: { type: "string" }
5447
+ },
5448
+ required: ["field_key", "label", "value"]
5449
+ }
5450
+ }
5451
+ }
5452
+ }
5453
+ },
5454
+ required: ["type"]
5455
+ }
5456
+ };
5280
5457
  var decisionCardSchema = z.object({
5281
5458
  type: z.literal("decision-card"),
5282
5459
  id: z.string().optional(),
@@ -5440,6 +5617,7 @@ var schemaRegistry = {
5440
5617
  "integrations-list": { schema: integrationsListSchema, tool: integrationsListTool },
5441
5618
  "pipeline-preview": { schema: pipelinePreviewSchema, tool: pipelinePreviewTool },
5442
5619
  "workflow-stepper": { schema: workflowStepperSchema, tool: workflowStepperTool },
5620
+ "document-field-extraction": { schema: documentFieldExtractionSchema, tool: documentFieldExtractionTool },
5443
5621
  "decision-card": { schema: decisionCardSchema, tool: decisionCardTool }
5444
5622
  };
5445
5623
 
@@ -6178,17 +6356,17 @@ function SparklineTableResolver(p) {
6178
6356
  // src/composites/heatmap-table/resolver.tsx
6179
6357
  init_theme();
6180
6358
  init_ThemeContext();
6181
- function heatColor(z59) {
6182
- const clamped = Math.max(-3, Math.min(3, z59));
6359
+ function heatColor(z60) {
6360
+ const clamped = Math.max(-3, Math.min(3, z60));
6183
6361
  const abs = Math.abs(clamped);
6184
6362
  const lightness = 95 - abs * 8;
6185
6363
  const hue = clamped >= 0 ? 142 : 0;
6186
6364
  return `hsl(${hue} 60% ${lightness}%)`;
6187
6365
  }
6188
- function heatTextColor(z59) {
6189
- const abs = Math.abs(z59);
6190
- if (abs > 2) return z59 >= 0 ? "#14532d" : "#7f1d1d";
6191
- if (abs > 1) return z59 >= 0 ? "#166534" : "#991b1b";
6366
+ function heatTextColor(z60) {
6367
+ const abs = Math.abs(z60);
6368
+ if (abs > 2) return z60 >= 0 ? "#14532d" : "#7f1d1d";
6369
+ if (abs > 1) return z60 >= 0 ? "#166534" : "#991b1b";
6192
6370
  return "var(--foreground)";
6193
6371
  }
6194
6372
  var th2 = {
@@ -6822,7 +7000,7 @@ function WaterfallChartResolver(p) {
6822
7000
  ] }) });
6823
7001
  }
6824
7002
  init_ThemeContext();
6825
- var FlowGraph2 = React41.lazy(
7003
+ var FlowGraph2 = React45.lazy(
6826
7004
  () => Promise.resolve().then(() => (init_FlowGraph(), FlowGraph_exports)).then((m) => ({ default: m.FlowGraph }))
6827
7005
  );
6828
7006
  function FlowCanvasRenderer({
@@ -7096,7 +7274,7 @@ function TrialBalanceResolver(p) {
7096
7274
  gDebit += a.debit;
7097
7275
  gCredit += a.credit;
7098
7276
  });
7099
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
7277
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
7100
7278
  /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 4, style: {
7101
7279
  padding: "8px 12px",
7102
7280
  fontSize: "12px",
@@ -8482,10 +8660,10 @@ function useFullscreen(ref) {
8482
8660
  // src/composites/document-preview/resolver.tsx
8483
8661
  init_theme();
8484
8662
  init_ThemeContext();
8485
- var PdfViewer2 = React41.lazy(
8663
+ var PdfViewer2 = React45.lazy(
8486
8664
  () => Promise.resolve().then(() => (init_PdfViewer(), PdfViewer_exports)).then((m) => ({ default: m.PdfViewer }))
8487
8665
  );
8488
- var ExcelViewer2 = React41.lazy(
8666
+ var ExcelViewer2 = React45.lazy(
8489
8667
  () => Promise.resolve().then(() => (init_ExcelViewer(), ExcelViewer_exports)).then((m) => ({ default: m.ExcelViewer }))
8490
8668
  );
8491
8669
  var highlightColors = {
@@ -9441,7 +9619,7 @@ function EngagementPipelineResolver(p) {
9441
9619
  const color = (_a2 = statusColor2[phase.status]) != null ? _a2 : "#d6d3d1";
9442
9620
  const isCurrent = i === activeIdx;
9443
9621
  const size = isCurrent ? 28 : 20;
9444
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
9622
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
9445
9623
  i > 0 && /* @__PURE__ */ jsx(
9446
9624
  "div",
9447
9625
  {
@@ -11604,7 +11782,7 @@ function FileCard({
11604
11782
  whiteSpace: "nowrap"
11605
11783
  },
11606
11784
  children: [
11607
- metaTokens.map((t, i) => /* @__PURE__ */ jsxs(React41.Fragment, { children: [
11785
+ metaTokens.map((t, i) => /* @__PURE__ */ jsxs(React45.Fragment, { children: [
11608
11786
  i > 0 && /* @__PURE__ */ jsx("span", { style: { color: "#c4c4c4" }, children: "\xB7" }),
11609
11787
  /* @__PURE__ */ jsx("span", { children: t })
11610
11788
  ] }, i)),
@@ -12179,7 +12357,7 @@ function KeyValueListResolver(p) {
12179
12357
  var _a3;
12180
12358
  return sum + ((_a3 = g.items) != null ? _a3 : []).length;
12181
12359
  }, 0);
12182
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12360
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12183
12361
  /* @__PURE__ */ jsxs(
12184
12362
  "div",
12185
12363
  {
@@ -12357,7 +12535,7 @@ function BalanceSheetResolver(p) {
12357
12535
  /* @__PURE__ */ jsxs("tbody", { children: [
12358
12536
  ((_p = p.sections) != null ? _p : []).map((section) => {
12359
12537
  var _a2;
12360
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12538
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12361
12539
  /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan, style: {
12362
12540
  padding: "8px 12px",
12363
12541
  fontSize: "12px",
@@ -12370,7 +12548,7 @@ function BalanceSheetResolver(p) {
12370
12548
  }, children: (_a2 = categoryLabels2[section.category]) != null ? _a2 : section.category }) }),
12371
12549
  section.subsections.map((sub, si) => {
12372
12550
  var _a3;
12373
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12551
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12374
12552
  /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan, style: {
12375
12553
  padding: "6px 10px 6px 24px",
12376
12554
  fontSize: "12px",
@@ -12546,7 +12724,7 @@ function IncomeStatementResolver(p) {
12546
12724
  textAlign: "left",
12547
12725
  whiteSpace: "nowrap"
12548
12726
  }, children: "\xA0" }),
12549
- periods.map((period) => /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12727
+ periods.map((period) => /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12550
12728
  /* @__PURE__ */ jsx("th", { style: {
12551
12729
  fontSize: "11px",
12552
12730
  fontWeight: 500,
@@ -12577,7 +12755,7 @@ function IncomeStatementResolver(p) {
12577
12755
  sections.map((section, si) => {
12578
12756
  var _a2;
12579
12757
  const isSubtotal = section.sectionType === "subtotal";
12580
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12758
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12581
12759
  /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: colCount, style: {
12582
12760
  padding: "8px 12px",
12583
12761
  fontSize: "12px",
@@ -12608,7 +12786,7 @@ function IncomeStatementResolver(p) {
12608
12786
  var _a4;
12609
12787
  const val = (_a4 = amounts[pi]) != null ? _a4 : 0;
12610
12788
  const isNeg = val < 0;
12611
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12789
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12612
12790
  /* @__PURE__ */ jsx("td", { style: {
12613
12791
  padding: "6px 10px",
12614
12792
  fontSize: "13px",
@@ -12643,7 +12821,7 @@ function IncomeStatementResolver(p) {
12643
12821
  var _a2;
12644
12822
  const val = (_a2 = grossProfit[pi]) != null ? _a2 : 0;
12645
12823
  const isNeg = val < 0;
12646
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12824
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12647
12825
  /* @__PURE__ */ jsx("td", { style: {
12648
12826
  padding: "8px 10px",
12649
12827
  fontSize: "12px",
@@ -12678,7 +12856,7 @@ function IncomeStatementResolver(p) {
12678
12856
  var _a2;
12679
12857
  const val = (_a2 = operatingIncome[pi]) != null ? _a2 : 0;
12680
12858
  const isNeg = val < 0;
12681
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12859
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12682
12860
  /* @__PURE__ */ jsx("td", { style: {
12683
12861
  padding: "8px 10px",
12684
12862
  fontSize: "12px",
@@ -12712,7 +12890,7 @@ function IncomeStatementResolver(p) {
12712
12890
  var _a2;
12713
12891
  const val = (_a2 = netIncome[pi]) != null ? _a2 : 0;
12714
12892
  const isNeg = val < 0;
12715
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
12893
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12716
12894
  /* @__PURE__ */ jsx("td", { style: {
12717
12895
  padding: "10px 10px",
12718
12896
  fontSize: "13px",
@@ -12932,7 +13110,7 @@ function CashFlowStatementResolver(p) {
12932
13110
  /* @__PURE__ */ jsxs("tbody", { children: [
12933
13111
  activities.map((activity, ai) => {
12934
13112
  var _a2, _b2, _c2;
12935
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
13113
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
12936
13114
  ai > 0 && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx(
12937
13115
  "td",
12938
13116
  {
@@ -17369,8 +17547,8 @@ var CARD_MIN = 132;
17369
17547
  var CARD_GAP = 16;
17370
17548
  var AUTO_COMPACT_BELOW = 360;
17371
17549
  function useContainerWidth(ref) {
17372
- const [w, setW] = React41.useState(0);
17373
- React41.useLayoutEffect(() => {
17550
+ const [w, setW] = React45.useState(0);
17551
+ React45.useLayoutEffect(() => {
17374
17552
  const el = ref.current;
17375
17553
  if (!el || typeof ResizeObserver === "undefined") return;
17376
17554
  const ro = new ResizeObserver((entries) => {
@@ -17471,15 +17649,15 @@ function WorkflowStepperRenderer({
17471
17649
  const interactive = typeof onSelectStep === "function";
17472
17650
  const statusColor3 = (s) => s === "active" ? ACCENT2 : STATUS_COLORS3[s];
17473
17651
  const doneCount = steps.filter((s) => s.status === "done").length;
17474
- const rootRef = React41.useRef(null);
17652
+ const rootRef = React45.useRef(null);
17475
17653
  const containerW = useContainerWidth(rootRef);
17476
17654
  let resolved = density === "auto" ? "full" : density;
17477
17655
  if (density === "auto" && containerW > 0) {
17478
17656
  const needed = steps.length * CARD_MIN + Math.max(0, steps.length - 1) * CARD_GAP;
17479
17657
  resolved = containerW < AUTO_COMPACT_BELOW || needed > containerW * 1.6 ? "compact" : "full";
17480
17658
  }
17481
- const scrollerRef = React41.useRef(null);
17482
- React41.useEffect(() => {
17659
+ const scrollerRef = React45.useRef(null);
17660
+ React45.useEffect(() => {
17483
17661
  if (resolved !== "full" || loading) return;
17484
17662
  const sc = scrollerRef.current;
17485
17663
  if (!sc) return;
@@ -17490,11 +17668,11 @@ function WorkflowStepperRenderer({
17490
17668
  el.scrollIntoView({ inline: "center", block: "nearest", behavior: "smooth" });
17491
17669
  }
17492
17670
  }, [resolved, loading, selectedStep, activeStepId]);
17493
- const [edges, setEdges] = React41.useState({
17671
+ const [edges, setEdges] = React45.useState({
17494
17672
  left: false,
17495
17673
  right: false
17496
17674
  });
17497
- const updateEdges = React41.useCallback(() => {
17675
+ const updateEdges = React45.useCallback(() => {
17498
17676
  const sc = scrollerRef.current;
17499
17677
  if (!sc) return;
17500
17678
  const max = sc.scrollWidth - sc.clientWidth;
@@ -17503,7 +17681,7 @@ function WorkflowStepperRenderer({
17503
17681
  const right = sl < max - 1;
17504
17682
  setEdges((prev) => prev.left === left && prev.right === right ? prev : { left, right });
17505
17683
  }, []);
17506
- React41.useLayoutEffect(() => {
17684
+ React45.useLayoutEffect(() => {
17507
17685
  if (resolved !== "full" || loading) return;
17508
17686
  updateEdges();
17509
17687
  const sc = scrollerRef.current;
@@ -17559,7 +17737,7 @@ function WorkflowStepperRenderer({
17559
17737
  const reached = s.status !== "pending";
17560
17738
  const clickable = interactive && reached;
17561
17739
  const size = act ? 11 : done ? 9 : 8;
17562
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
17740
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
17563
17741
  i > 0 && /* @__PURE__ */ jsx(
17564
17742
  "span",
17565
17743
  {
@@ -17702,7 +17880,7 @@ function WorkflowStepperRenderer({
17702
17880
  const color = statusColor3(step.status);
17703
17881
  const humans = ((_a2 = step.assignees) != null ? _a2 : []).filter((a) => a.kind === "human");
17704
17882
  const roleCaption = humans.map((h) => h.role).filter(Boolean).slice(0, 2).join(" \xB7 ");
17705
- return /* @__PURE__ */ jsxs(React41.Fragment, { children: [
17883
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
17706
17884
  i > 0 && /* @__PURE__ */ jsx(
17707
17885
  "div",
17708
17886
  {
@@ -17956,6 +18134,1562 @@ function WorkflowStepperResolver(p) {
17956
18134
  ] }) });
17957
18135
  }
17958
18136
  init_ThemeContext();
18137
+
18138
+ // src/composites/document-field-extraction/bboxTransform.ts
18139
+ function bboxToRect(bbox2, page, px) {
18140
+ if (!bbox2 || bbox2.length !== 4) return null;
18141
+ const { wPts, hPts } = page;
18142
+ const { wPx, hPx } = px;
18143
+ if (!wPts || !hPts || !wPx || !hPx) return null;
18144
+ const [x0, y0, x1, y1] = bbox2;
18145
+ const sx = wPx / wPts;
18146
+ const sy = hPx / hPts;
18147
+ return {
18148
+ left: x0 * sx,
18149
+ width: (x1 - x0) * sx,
18150
+ // Flip Y: the box's TOP in screen space is the page height minus its upper
18151
+ // PDF-point edge (y1), because PDF y grows upward from the bottom.
18152
+ top: (hPts - y1) * sy,
18153
+ height: (y1 - y0) * sy
18154
+ };
18155
+ }
18156
+
18157
+ // src/composites/document-field-extraction/FieldDetail.tsx
18158
+ init_ThemeContext();
18159
+
18160
+ // src/composites/document-field-extraction/fieldLabels.ts
18161
+ var LABEL_DISPLAY = {
18162
+ "employer identification number": "Employer ID (EIN)",
18163
+ "company or business name": "Company Name",
18164
+ "total revenue": "Total Revenue",
18165
+ "net income": "Net Income",
18166
+ "tax year": "Tax Year",
18167
+ "invoice number": "Invoice Number",
18168
+ "invoice date": "Invoice Date",
18169
+ "total amount due": "Total Amount Due",
18170
+ "vendor name": "Vendor Name",
18171
+ "receipt total": "Receipt Total",
18172
+ "transaction date": "Transaction Date",
18173
+ "merchant name": "Merchant Name"
18174
+ };
18175
+ function prettyLabel(label) {
18176
+ var _a;
18177
+ return (_a = LABEL_DISPLAY[label]) != null ? _a : label.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());
18178
+ }
18179
+ var TIER_COLORS = {
18180
+ green: "#15803d",
18181
+ amber: "#f59e0b",
18182
+ red: "#dc2626"
18183
+ };
18184
+ var BADGE_TITLE = {
18185
+ SC: "High confidence",
18186
+ CN: "Medium confidence",
18187
+ AB: "Low confidence",
18188
+ SW: "Very low confidence"
18189
+ };
18190
+ var BADGE_STYLE = {
18191
+ SC: { bg: "#dcfce7", fg: "#15803d" },
18192
+ CN: { bg: "#eff6ff", fg: "#0364ff" },
18193
+ AB: { bg: "#fff7ed", fg: "#92400e" },
18194
+ SW: { bg: "#fef2f2", fg: "#dc2626" }
18195
+ };
18196
+ var STATE_DOT = {
18197
+ ok: "#15803d",
18198
+ corrected: "#0364ff",
18199
+ missing: "#dc2626",
18200
+ warn: "#f59e0b"
18201
+ };
18202
+ function hexToRgba(hex, alpha) {
18203
+ const h = hex.replace("#", "");
18204
+ const r = parseInt(h.slice(0, 2), 16);
18205
+ const g = parseInt(h.slice(2, 4), 16);
18206
+ const b = parseInt(h.slice(4, 6), 16);
18207
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
18208
+ }
18209
+ function badgeStyle(f) {
18210
+ var _a, _b;
18211
+ return (_b = BADGE_STYLE[(_a = f.badge) != null ? _a : ""]) != null ? _b : { bg: "#f6f6f6", fg: "#777777" };
18212
+ }
18213
+ function dotColor2(f) {
18214
+ var _a, _b;
18215
+ return (_b = STATE_DOT[(_a = f.state) != null ? _a : "ok"]) != null ? _b : "#15803d";
18216
+ }
18217
+ function fieldColor(f, secondary) {
18218
+ var _a, _b;
18219
+ if (f.state === "corrected") return secondary;
18220
+ return (_b = TIER_COLORS[(_a = f.confidence_tier) != null ? _a : "amber"]) != null ? _b : TIER_COLORS.amber;
18221
+ }
18222
+ function confidenceLabel(f) {
18223
+ var _a;
18224
+ if (typeof f.confidence === "number" && !Number.isNaN(f.confidence)) {
18225
+ return `${Math.round(f.confidence * 100)}%`;
18226
+ }
18227
+ return (_a = f.badge) != null ? _a : "\u2014";
18228
+ }
18229
+ function FieldDetail({
18230
+ field,
18231
+ selected,
18232
+ onSelect,
18233
+ onSave,
18234
+ correctionCount = 0,
18235
+ cardRef
18236
+ }) {
18237
+ var _a, _b, _c, _d;
18238
+ const { BORDER: BORDER4, MUTED: MUTED2, PAPER: PAPER2, SECONDARY: SECONDARY2 } = useTheme();
18239
+ const [editing, setEditing] = React45.useState(false);
18240
+ const [draft, setDraft] = React45.useState("");
18241
+ const [saving, setSaving] = React45.useState(false);
18242
+ const [saveError, setSaveError] = React45.useState(null);
18243
+ const textareaRef = React45.useRef(null);
18244
+ const color = fieldColor(field, SECONDARY2);
18245
+ const corrected = field.state === "corrected";
18246
+ const startEdit = (e) => {
18247
+ e.stopPropagation();
18248
+ setDraft(field.value || "");
18249
+ setSaveError(null);
18250
+ setEditing(true);
18251
+ setTimeout(() => {
18252
+ var _a2, _b2;
18253
+ (_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
18254
+ (_b2 = textareaRef.current) == null ? void 0 : _b2.select();
18255
+ }, 0);
18256
+ };
18257
+ const cancelEdit = (e) => {
18258
+ e.stopPropagation();
18259
+ setEditing(false);
18260
+ setSaveError(null);
18261
+ };
18262
+ const save = async (e) => {
18263
+ var _a2, _b2;
18264
+ e.stopPropagation();
18265
+ if (!onSave || draft === field.value) {
18266
+ setEditing(false);
18267
+ return;
18268
+ }
18269
+ setSaving(true);
18270
+ setSaveError(null);
18271
+ try {
18272
+ await onSave(field.field_key, draft);
18273
+ setEditing(false);
18274
+ } catch (err) {
18275
+ const msg = ((_b2 = (_a2 = err == null ? void 0 : err.response) == null ? void 0 : _a2.data) == null ? void 0 : _b2.detail) || "Save failed. Try again.";
18276
+ setSaveError(msg);
18277
+ } finally {
18278
+ setSaving(false);
18279
+ }
18280
+ };
18281
+ const handleKeyDown = (e) => {
18282
+ if (e.key === "Escape") cancelEdit(e);
18283
+ if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) void save(e);
18284
+ };
18285
+ return /* @__PURE__ */ jsxs(
18286
+ "div",
18287
+ {
18288
+ ref: cardRef,
18289
+ "data-testid": "field-card",
18290
+ "data-field-key": field.field_key,
18291
+ "data-selected": selected ? "true" : "false",
18292
+ role: "button",
18293
+ tabIndex: 0,
18294
+ onClick: () => !editing && onSelect(selected ? null : field.field_key),
18295
+ onKeyDown: (e) => {
18296
+ if (!editing && e.key === "Enter") onSelect(selected ? null : field.field_key);
18297
+ },
18298
+ style: {
18299
+ display: "flex",
18300
+ flexDirection: "column",
18301
+ gap: "4px",
18302
+ padding: "8px 10px",
18303
+ borderRadius: "0.5rem",
18304
+ border: `1px solid ${selected ? color : BORDER4}`,
18305
+ background: selected ? hexToRgba(color, 0.06) : "white",
18306
+ cursor: editing ? "default" : "pointer",
18307
+ transition: "background 0.15s, border-color 0.15s"
18308
+ },
18309
+ children: [
18310
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", minWidth: 0 }, children: [
18311
+ /* @__PURE__ */ jsx(
18312
+ "span",
18313
+ {
18314
+ title: (_c = (_b = BADGE_TITLE[(_a = field.badge) != null ? _a : ""]) != null ? _b : field.badge) != null ? _c : "",
18315
+ style: {
18316
+ fontSize: "10px",
18317
+ fontWeight: 700,
18318
+ padding: "2px 6px",
18319
+ borderRadius: "4px",
18320
+ flexShrink: 0,
18321
+ letterSpacing: "0.02em",
18322
+ minWidth: "34px",
18323
+ textAlign: "center",
18324
+ fontVariantNumeric: "tabular-nums",
18325
+ background: badgeStyle(field).bg,
18326
+ color: badgeStyle(field).fg
18327
+ },
18328
+ children: confidenceLabel(field)
18329
+ }
18330
+ ),
18331
+ /* @__PURE__ */ jsx(
18332
+ "span",
18333
+ {
18334
+ title: prettyLabel(field.label),
18335
+ style: {
18336
+ fontSize: "12.5px",
18337
+ fontWeight: 600,
18338
+ color: "var(--foreground)",
18339
+ flex: 1,
18340
+ minWidth: 0,
18341
+ whiteSpace: "nowrap",
18342
+ overflow: "hidden",
18343
+ textOverflow: "ellipsis"
18344
+ },
18345
+ children: prettyLabel(field.label)
18346
+ }
18347
+ ),
18348
+ field.is_primary && /* @__PURE__ */ jsx(
18349
+ "span",
18350
+ {
18351
+ style: {
18352
+ fontSize: "9px",
18353
+ fontWeight: 600,
18354
+ textTransform: "uppercase",
18355
+ letterSpacing: "0.04em",
18356
+ color: MUTED2,
18357
+ flexShrink: 0
18358
+ },
18359
+ children: "Primary"
18360
+ }
18361
+ ),
18362
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: "11px", color: MUTED2, flexShrink: 0 }, children: [
18363
+ "p.",
18364
+ field.page + 1
18365
+ ] }),
18366
+ !editing && onSave && /* @__PURE__ */ jsx(
18367
+ "button",
18368
+ {
18369
+ type: "button",
18370
+ onClick: startEdit,
18371
+ title: "Edit value",
18372
+ "aria-label": "Edit value",
18373
+ style: {
18374
+ border: `1px solid ${BORDER4}`,
18375
+ borderRadius: "6px",
18376
+ background: "white",
18377
+ cursor: "pointer",
18378
+ padding: "3px 5px",
18379
+ lineHeight: 1,
18380
+ flexShrink: 0,
18381
+ display: "inline-flex",
18382
+ alignItems: "center",
18383
+ justifyContent: "center"
18384
+ },
18385
+ children: /* @__PURE__ */ jsxs("svg", { width: "12", height: "12", viewBox: "0 0 24 24", fill: "none", stroke: MUTED2, strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
18386
+ /* @__PURE__ */ jsx("path", { d: "M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" }),
18387
+ /* @__PURE__ */ jsx("path", { d: "M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z" })
18388
+ ] })
18389
+ }
18390
+ )
18391
+ ] }),
18392
+ editing ? /* @__PURE__ */ jsxs("div", { onClick: (e) => e.stopPropagation(), style: { display: "flex", flexDirection: "column", gap: "5px" }, children: [
18393
+ /* @__PURE__ */ jsx(
18394
+ "textarea",
18395
+ {
18396
+ ref: textareaRef,
18397
+ value: draft,
18398
+ onChange: (e) => setDraft(e.target.value),
18399
+ onKeyDown: handleKeyDown,
18400
+ rows: 3,
18401
+ placeholder: "Enter value\u2026",
18402
+ style: {
18403
+ width: "100%",
18404
+ boxSizing: "border-box",
18405
+ resize: "vertical",
18406
+ fontSize: "13px",
18407
+ fontFamily: "var(--font-sans)",
18408
+ color: "var(--foreground)",
18409
+ padding: "6px 8px",
18410
+ borderRadius: "0.4rem",
18411
+ border: `1px solid ${BORDER4}`
18412
+ }
18413
+ }
18414
+ ),
18415
+ saveError && /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: TIER_COLORS.red }, children: saveError }),
18416
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "6px" }, children: [
18417
+ /* @__PURE__ */ jsx(
18418
+ "button",
18419
+ {
18420
+ type: "button",
18421
+ onClick: save,
18422
+ disabled: saving,
18423
+ title: "Save (\u2318\u21B5)",
18424
+ style: {
18425
+ display: "inline-flex",
18426
+ alignItems: "center",
18427
+ gap: "4px",
18428
+ fontSize: "11px",
18429
+ fontWeight: 600,
18430
+ padding: "4px 10px",
18431
+ borderRadius: "0.4rem",
18432
+ border: "none",
18433
+ background: "var(--foreground)",
18434
+ color: "white",
18435
+ cursor: saving ? "default" : "pointer",
18436
+ opacity: saving ? 0.7 : 1
18437
+ },
18438
+ children: saving ? "Saving\u2026" : "Save"
18439
+ }
18440
+ ),
18441
+ /* @__PURE__ */ jsx(
18442
+ "button",
18443
+ {
18444
+ type: "button",
18445
+ onClick: cancelEdit,
18446
+ disabled: saving,
18447
+ title: "Cancel (Esc)",
18448
+ style: {
18449
+ fontSize: "11px",
18450
+ fontWeight: 600,
18451
+ padding: "4px 10px",
18452
+ borderRadius: "0.4rem",
18453
+ border: `1px solid ${BORDER4}`,
18454
+ background: "white",
18455
+ color: MUTED2,
18456
+ cursor: saving ? "default" : "pointer"
18457
+ },
18458
+ children: "Cancel"
18459
+ }
18460
+ )
18461
+ ] })
18462
+ ] }) : corrected ? /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "2px", paddingLeft: "2px" }, children: [
18463
+ field.previous_value != null && field.previous_value !== "" && /* @__PURE__ */ jsx(
18464
+ "span",
18465
+ {
18466
+ style: {
18467
+ fontSize: "11.5px",
18468
+ color: MUTED2,
18469
+ textDecoration: "line-through",
18470
+ fontFamily: "ui-monospace, 'Cascadia Code', monospace",
18471
+ wordBreak: "break-word"
18472
+ },
18473
+ children: field.previous_value
18474
+ }
18475
+ ),
18476
+ /* @__PURE__ */ jsx(
18477
+ "span",
18478
+ {
18479
+ style: {
18480
+ fontSize: "12.5px",
18481
+ fontWeight: 600,
18482
+ color: "var(--foreground)",
18483
+ fontFamily: "ui-monospace, 'Cascadia Code', monospace",
18484
+ wordBreak: "break-word"
18485
+ },
18486
+ children: field.value || "\u2014"
18487
+ }
18488
+ ),
18489
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
18490
+ field.corrected_by && /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: SECONDARY2 }, title: `Corrected by ${field.corrected_by}`, children: field.corrected_by }),
18491
+ correctionCount > 0 && /* @__PURE__ */ jsxs(
18492
+ "span",
18493
+ {
18494
+ style: {
18495
+ fontSize: "10.5px",
18496
+ fontWeight: 600,
18497
+ color: MUTED2,
18498
+ background: PAPER2,
18499
+ borderRadius: "9999px",
18500
+ padding: "1px 7px"
18501
+ },
18502
+ children: [
18503
+ correctionCount,
18504
+ " ",
18505
+ correctionCount === 1 ? "event" : "events"
18506
+ ]
18507
+ }
18508
+ )
18509
+ ] })
18510
+ ] }) : (
18511
+ // Bottom row: monospace value + right-edge validation dot (file explorer).
18512
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "8px", paddingLeft: "2px" }, children: [
18513
+ /* @__PURE__ */ jsx(
18514
+ "span",
18515
+ {
18516
+ style: {
18517
+ fontSize: "12.5px",
18518
+ color: MUTED2,
18519
+ fontFamily: "ui-monospace, 'Cascadia Code', monospace",
18520
+ flex: 1,
18521
+ minWidth: 0,
18522
+ whiteSpace: "nowrap",
18523
+ overflow: "hidden",
18524
+ textOverflow: "ellipsis"
18525
+ },
18526
+ title: field.value || "\u2014",
18527
+ children: field.value || "\u2014"
18528
+ }
18529
+ ),
18530
+ /* @__PURE__ */ jsx(
18531
+ "span",
18532
+ {
18533
+ title: (_d = field.state) != null ? _d : "ok",
18534
+ style: {
18535
+ width: "8px",
18536
+ height: "8px",
18537
+ borderRadius: "9999px",
18538
+ flexShrink: 0,
18539
+ background: dotColor2(field)
18540
+ }
18541
+ }
18542
+ )
18543
+ ] })
18544
+ )
18545
+ ]
18546
+ }
18547
+ );
18548
+ }
18549
+
18550
+ // src/composites/document-field-extraction/FieldDetails.tsx
18551
+ init_ThemeContext();
18552
+ var BADGE_DOT = {
18553
+ // gen-ui semantic dot colors
18554
+ ok: "#15803d",
18555
+ green: "#15803d",
18556
+ warn: "#f59e0b",
18557
+ amber: "#f59e0b",
18558
+ missing: "#dc2626",
18559
+ red: "#dc2626",
18560
+ corrected: "#0364ff"
18561
+ };
18562
+ function FieldDetails({
18563
+ summary,
18564
+ fileName,
18565
+ pageCount,
18566
+ pagesProcessed
18567
+ }) {
18568
+ var _a, _b, _c, _d, _e, _f;
18569
+ const { BORDER: BORDER4, MUTED: MUTED2, PAPER: PAPER2, SECONDARY: SECONDARY2 } = useTheme();
18570
+ if (!summary) {
18571
+ return /* @__PURE__ */ jsxs(
18572
+ "div",
18573
+ {
18574
+ style: {
18575
+ height: "100%",
18576
+ display: "flex",
18577
+ flexDirection: "column",
18578
+ alignItems: "center",
18579
+ justifyContent: "center",
18580
+ gap: "6px",
18581
+ color: MUTED2,
18582
+ textAlign: "center",
18583
+ padding: "24px"
18584
+ },
18585
+ children: [
18586
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "26px", lineHeight: 1 }, children: "\u{1F4C4}" }),
18587
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "14px", fontWeight: 600, color: "var(--foreground)" }, children: "No summary yet" }),
18588
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "12px", maxWidth: "280px" }, children: "This document has no extracted summary." })
18589
+ ]
18590
+ }
18591
+ );
18592
+ }
18593
+ const header = buildHeader(summary);
18594
+ const crumb = buildCrumbLine(summary);
18595
+ const facts = buildFacts(summary, fileName, pageCount, pagesProcessed);
18596
+ return /* @__PURE__ */ jsx("div", { style: { height: "100%", overflowY: "auto", paddingRight: "4px" }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "14px" }, children: [
18597
+ /* @__PURE__ */ jsxs("header", { style: { display: "flex", flexDirection: "column", gap: "3px" }, children: [
18598
+ /* @__PURE__ */ jsx(
18599
+ "h2",
18600
+ {
18601
+ title: header.title,
18602
+ style: {
18603
+ margin: 0,
18604
+ fontFamily: "var(--font-serif)",
18605
+ fontSize: "20px",
18606
+ fontWeight: 400,
18607
+ letterSpacing: "-0.01em",
18608
+ color: "var(--foreground)",
18609
+ overflow: "hidden",
18610
+ textOverflow: "ellipsis",
18611
+ whiteSpace: "nowrap"
18612
+ },
18613
+ children: header.title
18614
+ }
18615
+ ),
18616
+ header.subtitle && /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "12px", color: MUTED2 }, children: header.subtitle })
18617
+ ] }),
18618
+ (summary.domain || summary.display_name || summary.domain_group || ((_b = (_a = summary.tags) == null ? void 0 : _a.length) != null ? _b : 0) > 0) && /* @__PURE__ */ jsxs(
18619
+ "section",
18620
+ {
18621
+ style: {
18622
+ display: "flex",
18623
+ flexDirection: "column",
18624
+ gap: "6px",
18625
+ padding: "12px 14px",
18626
+ borderRadius: "0.75rem",
18627
+ border: `1px solid ${BORDER4}`,
18628
+ background: PAPER2
18629
+ },
18630
+ children: [
18631
+ summary.domain_group && /* @__PURE__ */ jsx(
18632
+ "span",
18633
+ {
18634
+ style: {
18635
+ fontSize: "10px",
18636
+ fontWeight: 600,
18637
+ textTransform: "uppercase",
18638
+ letterSpacing: "0.06em",
18639
+ color: MUTED2
18640
+ },
18641
+ children: summary.domain_group
18642
+ }
18643
+ ),
18644
+ /* @__PURE__ */ jsx(
18645
+ "h3",
18646
+ {
18647
+ style: {
18648
+ margin: 0,
18649
+ fontSize: "14px",
18650
+ fontWeight: 600,
18651
+ color: "var(--foreground)"
18652
+ },
18653
+ children: summary.domain || summary.display_name
18654
+ }
18655
+ ),
18656
+ crumb && /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "12px", color: MUTED2 }, children: crumb }),
18657
+ ((_d = (_c = summary.tags) == null ? void 0 : _c.length) != null ? _d : 0) > 0 && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "5px", marginTop: "2px" }, children: summary.tags.map((t) => /* @__PURE__ */ jsx(
18658
+ "span",
18659
+ {
18660
+ style: {
18661
+ fontSize: "10px",
18662
+ fontWeight: 600,
18663
+ padding: "2px 8px",
18664
+ borderRadius: "9999px",
18665
+ background: "white",
18666
+ border: `1px solid ${BORDER4}`,
18667
+ color: MUTED2
18668
+ },
18669
+ children: t
18670
+ },
18671
+ t
18672
+ )) })
18673
+ ]
18674
+ }
18675
+ ),
18676
+ ((_f = (_e = summary.stats) == null ? void 0 : _e.length) != null ? _f : 0) > 0 && /* @__PURE__ */ jsx(
18677
+ "div",
18678
+ {
18679
+ style: {
18680
+ display: "grid",
18681
+ gridTemplateColumns: "repeat(auto-fit, minmax(130px, 1fr))",
18682
+ gap: "8px"
18683
+ },
18684
+ children: summary.stats.map((s) => /* @__PURE__ */ jsxs(
18685
+ "div",
18686
+ {
18687
+ style: {
18688
+ display: "flex",
18689
+ flexDirection: "column",
18690
+ gap: "2px",
18691
+ padding: "10px 12px",
18692
+ borderRadius: "0.6rem",
18693
+ border: `1px solid ${BORDER4}`,
18694
+ background: "white"
18695
+ },
18696
+ children: [
18697
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: MUTED2 }, children: s.label }),
18698
+ /* @__PURE__ */ jsx(
18699
+ "span",
18700
+ {
18701
+ title: s.value,
18702
+ style: {
18703
+ fontSize: "15px",
18704
+ fontWeight: 600,
18705
+ color: "var(--foreground)",
18706
+ overflow: "hidden",
18707
+ textOverflow: "ellipsis",
18708
+ whiteSpace: "nowrap"
18709
+ },
18710
+ children: s.value
18711
+ }
18712
+ ),
18713
+ s.caption && /* @__PURE__ */ jsx("span", { style: { fontSize: "10px", color: MUTED2 }, children: s.caption })
18714
+ ]
18715
+ },
18716
+ s.field_key
18717
+ ))
18718
+ }
18719
+ ),
18720
+ facts.length > 0 && /* @__PURE__ */ jsxs("section", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: [
18721
+ /* @__PURE__ */ jsxs(
18722
+ "div",
18723
+ {
18724
+ style: {
18725
+ display: "flex",
18726
+ alignItems: "center",
18727
+ gap: "6px",
18728
+ fontSize: "11px",
18729
+ fontWeight: 600,
18730
+ textTransform: "uppercase",
18731
+ letterSpacing: "0.04em",
18732
+ color: MUTED2
18733
+ },
18734
+ children: [
18735
+ /* @__PURE__ */ jsx("span", { children: "\u25A4" }),
18736
+ /* @__PURE__ */ jsx("span", { children: "Document details" })
18737
+ ]
18738
+ }
18739
+ ),
18740
+ /* @__PURE__ */ jsx(
18741
+ "dl",
18742
+ {
18743
+ style: {
18744
+ margin: 0,
18745
+ display: "grid",
18746
+ gridTemplateColumns: "minmax(110px, auto) 1fr",
18747
+ rowGap: "6px",
18748
+ columnGap: "14px"
18749
+ },
18750
+ children: facts.map((f) => {
18751
+ var _a2;
18752
+ return /* @__PURE__ */ jsxs(React45.Fragment, { children: [
18753
+ /* @__PURE__ */ jsx("dt", { style: { fontSize: "12px", color: MUTED2 }, children: f.label }),
18754
+ /* @__PURE__ */ jsxs(
18755
+ "dd",
18756
+ {
18757
+ title: typeof f.value === "string" ? f.value : void 0,
18758
+ style: {
18759
+ margin: 0,
18760
+ fontSize: "12px",
18761
+ color: "var(--foreground)",
18762
+ display: "flex",
18763
+ alignItems: "center",
18764
+ gap: "6px",
18765
+ wordBreak: "break-word"
18766
+ },
18767
+ children: [
18768
+ f.badge && /* @__PURE__ */ jsx(
18769
+ "span",
18770
+ {
18771
+ style: {
18772
+ width: "7px",
18773
+ height: "7px",
18774
+ borderRadius: "9999px",
18775
+ flexShrink: 0,
18776
+ background: (_a2 = BADGE_DOT[f.badge]) != null ? _a2 : SECONDARY2
18777
+ }
18778
+ }
18779
+ ),
18780
+ f.value
18781
+ ]
18782
+ }
18783
+ )
18784
+ ] }, f.key);
18785
+ })
18786
+ }
18787
+ )
18788
+ ] })
18789
+ ] }) });
18790
+ }
18791
+ function buildHeader(summary) {
18792
+ const form = [summary.short_name, summary.display_name].filter(Boolean);
18793
+ const title = summary.organization || form.shift() || summary.domain || "Document";
18794
+ const parts = summary.organization ? [...form] : form;
18795
+ if (summary.tax_year) parts.push(`TY ${summary.tax_year}`);
18796
+ return { title, subtitle: parts.join(" \xB7 ") };
18797
+ }
18798
+ function buildCrumbLine(summary) {
18799
+ const parts = [...summary.crumbs || [], summary.short_name].filter(Boolean);
18800
+ if (parts.length > 0) return parts.join(" \xB7 ");
18801
+ return summary.domain && summary.display_name ? summary.display_name : "";
18802
+ }
18803
+ function buildFacts(summary, fileName, pageCount, pagesProcessed) {
18804
+ const rows = [];
18805
+ if (fileName) rows.push({ key: "file", label: "File", value: fileName });
18806
+ if (summary.display_name) {
18807
+ rows.push({ key: "doctype", label: "Document type", value: summary.display_name });
18808
+ }
18809
+ if (summary.tax_year != null && summary.tax_year !== "") {
18810
+ rows.push({ key: "year", label: "Tax year", value: String(summary.tax_year) });
18811
+ }
18812
+ for (const f of summary.facts || []) {
18813
+ rows.push({ key: f.field_key, label: f.label, value: f.value, badge: f.badge });
18814
+ }
18815
+ if (pageCount) {
18816
+ rows.push({
18817
+ key: "pages",
18818
+ label: "Pages processed",
18819
+ value: `${pagesProcessed != null ? pagesProcessed : pageCount} / ${pageCount}`
18820
+ });
18821
+ }
18822
+ return rows;
18823
+ }
18824
+
18825
+ // src/composites/document-field-extraction/FieldAuditLog.tsx
18826
+ init_ThemeContext();
18827
+ function initials(name) {
18828
+ return name.trim().split(/\s+/).slice(0, 2).map((w) => {
18829
+ var _a;
18830
+ return ((_a = w[0]) != null ? _a : "").toUpperCase();
18831
+ }).join("");
18832
+ }
18833
+ function formatTs(ts) {
18834
+ if (!ts) return "\u2014";
18835
+ const d = new Date(ts);
18836
+ if (Number.isNaN(d.getTime())) return ts;
18837
+ return d.toLocaleString(void 0, {
18838
+ month: "short",
18839
+ day: "numeric",
18840
+ hour: "2-digit",
18841
+ minute: "2-digit"
18842
+ });
18843
+ }
18844
+ function FieldAuditLog({
18845
+ events = [],
18846
+ loading = false,
18847
+ fields = []
18848
+ }) {
18849
+ const { BORDER: BORDER4, MUTED: MUTED2, PAPER: PAPER2, SECONDARY: SECONDARY2 } = useTheme();
18850
+ const fieldLabelMap = React45.useMemo(() => {
18851
+ var _a;
18852
+ const map = {};
18853
+ for (const f of fields) map[f.field_key] = (_a = f.label) != null ? _a : f.field_key;
18854
+ return map;
18855
+ }, [fields]);
18856
+ if (loading) {
18857
+ return /* @__PURE__ */ jsx(
18858
+ "div",
18859
+ {
18860
+ style: {
18861
+ flex: 1,
18862
+ display: "flex",
18863
+ alignItems: "center",
18864
+ justifyContent: "center",
18865
+ padding: "32px 20px",
18866
+ fontSize: "13px",
18867
+ color: MUTED2
18868
+ },
18869
+ children: "Loading audit trail\u2026"
18870
+ }
18871
+ );
18872
+ }
18873
+ if (events.length === 0) {
18874
+ return /* @__PURE__ */ jsxs(
18875
+ "div",
18876
+ {
18877
+ style: {
18878
+ flex: 1,
18879
+ display: "flex",
18880
+ flexDirection: "column",
18881
+ alignItems: "center",
18882
+ justifyContent: "center",
18883
+ gap: "6px",
18884
+ padding: "32px 20px",
18885
+ textAlign: "center"
18886
+ },
18887
+ children: [
18888
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "13.5px", fontWeight: 600, color: "var(--foreground)" }, children: "No changes yet" }),
18889
+ /* @__PURE__ */ jsx("p", { style: { margin: 0, fontSize: "12px", color: MUTED2, maxWidth: "240px", lineHeight: 1.5 }, children: "Field corrections will appear here." })
18890
+ ]
18891
+ }
18892
+ );
18893
+ }
18894
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", flex: 1, minHeight: 0, overflow: "hidden" }, children: [
18895
+ /* @__PURE__ */ jsx("div", { style: { padding: "8px 14px 6px", flexShrink: 0 }, children: /* @__PURE__ */ jsxs(
18896
+ "span",
18897
+ {
18898
+ style: {
18899
+ fontSize: "11.5px",
18900
+ fontWeight: 600,
18901
+ color: MUTED2,
18902
+ textTransform: "uppercase",
18903
+ letterSpacing: "0.04em"
18904
+ },
18905
+ children: [
18906
+ events.length,
18907
+ " ",
18908
+ events.length === 1 ? "change" : "changes"
18909
+ ]
18910
+ }
18911
+ ) }),
18912
+ /* @__PURE__ */ jsx("ol", { style: { listStyle: "none", margin: 0, padding: "0 0 16px", overflowY: "auto", flex: 1, minHeight: 0 }, children: events.map((ev) => {
18913
+ var _a, _b;
18914
+ const rawLabel = (_a = fieldLabelMap[ev.field_key]) != null ? _a : ev.field_key;
18915
+ const label = prettyLabel(rawLabel);
18916
+ const isAI = ev.actor_type === "system" || ev.event_type === "ai_corrected" || ev.event_type === "ai_extracted";
18917
+ const actor = isAI ? "AI" : ev.actor_name || "Unknown";
18918
+ const eventLabel = isAI ? "Re-extracted" : "Corrected";
18919
+ return /* @__PURE__ */ jsxs(
18920
+ "li",
18921
+ {
18922
+ "data-testid": "audit-row",
18923
+ style: { display: "flex", gap: "10px", padding: "10px 14px", borderBottom: `1px solid ${BORDER4}` },
18924
+ children: [
18925
+ /* @__PURE__ */ jsx(
18926
+ "div",
18927
+ {
18928
+ title: isAI ? "AI extraction" : actor,
18929
+ style: {
18930
+ flexShrink: 0,
18931
+ width: "28px",
18932
+ height: "28px",
18933
+ borderRadius: "50%",
18934
+ background: isAI ? "#dcfce7" : "#eff6ff",
18935
+ color: isAI ? "#15803d" : SECONDARY2,
18936
+ fontSize: "10.5px",
18937
+ fontWeight: 700,
18938
+ display: "flex",
18939
+ alignItems: "center",
18940
+ justifyContent: "center",
18941
+ marginTop: "1px"
18942
+ },
18943
+ children: isAI ? "\u2728" : initials(actor)
18944
+ }
18945
+ ),
18946
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "3px", flex: 1, minWidth: 0 }, children: [
18947
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "baseline", gap: "6px", flexWrap: "wrap" }, children: [
18948
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "12.5px", fontWeight: 600, color: isAI ? "#15803d" : "var(--foreground)" }, children: actor }),
18949
+ /* @__PURE__ */ jsx(
18950
+ "span",
18951
+ {
18952
+ style: {
18953
+ fontSize: "10.5px",
18954
+ fontWeight: 600,
18955
+ color: MUTED2,
18956
+ background: PAPER2,
18957
+ borderRadius: "4px",
18958
+ padding: "1px 6px",
18959
+ whiteSpace: "nowrap",
18960
+ flexShrink: 0
18961
+ },
18962
+ children: eventLabel
18963
+ }
18964
+ ),
18965
+ /* @__PURE__ */ jsx(
18966
+ "span",
18967
+ {
18968
+ style: {
18969
+ fontSize: "12px",
18970
+ color: MUTED2,
18971
+ flex: 1,
18972
+ minWidth: 0,
18973
+ whiteSpace: "nowrap",
18974
+ overflow: "hidden",
18975
+ textOverflow: "ellipsis"
18976
+ },
18977
+ children: label
18978
+ }
18979
+ ),
18980
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: MUTED2, flexShrink: 0 }, children: formatTs(ev.created_at) })
18981
+ ] }),
18982
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: "6px", flexWrap: "wrap" }, children: [
18983
+ ev.previous_value != null && /* @__PURE__ */ jsxs(Fragment, { children: [
18984
+ /* @__PURE__ */ jsx(
18985
+ "span",
18986
+ {
18987
+ style: {
18988
+ fontSize: "12px",
18989
+ fontFamily: "ui-monospace, monospace",
18990
+ color: MUTED2,
18991
+ textDecoration: "line-through"
18992
+ },
18993
+ children: ev.previous_value
18994
+ }
18995
+ ),
18996
+ /* @__PURE__ */ jsx("span", { style: { fontSize: "11px", color: BORDER4 }, children: "\u2192" })
18997
+ ] }),
18998
+ /* @__PURE__ */ jsx(
18999
+ "span",
19000
+ {
19001
+ style: {
19002
+ fontSize: "12px",
19003
+ fontFamily: "ui-monospace, monospace",
19004
+ fontWeight: 600,
19005
+ color: "var(--foreground)"
19006
+ },
19007
+ children: (_b = ev.new_value) != null ? _b : "\u2014"
19008
+ }
19009
+ )
19010
+ ] }),
19011
+ ev.reason && /* @__PURE__ */ jsxs("p", { style: { margin: "2px 0 0", fontSize: "11.5px", color: MUTED2, fontStyle: "italic" }, children: [
19012
+ "\u201C",
19013
+ ev.reason,
19014
+ "\u201D"
19015
+ ] })
19016
+ ] })
19017
+ ]
19018
+ },
19019
+ ev.id
19020
+ );
19021
+ }) })
19022
+ ] });
19023
+ }
19024
+ var TIER_COLORS2 = {
19025
+ green: "#15803d",
19026
+ amber: "#f59e0b",
19027
+ red: "#dc2626"
19028
+ };
19029
+ function fieldColor2(f, secondary) {
19030
+ var _a, _b;
19031
+ if (f.state === "corrected") return secondary;
19032
+ return (_b = TIER_COLORS2[(_a = f.confidence_tier) != null ? _a : "amber"]) != null ? _b : TIER_COLORS2.amber;
19033
+ }
19034
+ function hexToRgba2(hex, alpha) {
19035
+ const h = hex.replace("#", "");
19036
+ const r = parseInt(h.slice(0, 2), 16);
19037
+ const g = parseInt(h.slice(2, 4), 16);
19038
+ const b = parseInt(h.slice(4, 6), 16);
19039
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
19040
+ }
19041
+ function PageOverlay({
19042
+ page,
19043
+ fields,
19044
+ selectedKey,
19045
+ onSelect,
19046
+ secondary
19047
+ }) {
19048
+ const { PAPER: PAPER2, MUTED: MUTED2 } = useTheme();
19049
+ const imgRef = React45.useRef(null);
19050
+ const selectedBoxRef = React45.useRef(null);
19051
+ const [px, setPx] = React45.useState(null);
19052
+ React45.useEffect(() => {
19053
+ const el = imgRef.current;
19054
+ if (!el) return;
19055
+ const measure = () => {
19056
+ if (el.clientWidth && el.clientHeight) {
19057
+ setPx({ wPx: el.clientWidth, hPx: el.clientHeight });
19058
+ }
19059
+ };
19060
+ measure();
19061
+ if (typeof ResizeObserver === "undefined") return;
19062
+ const ro = new ResizeObserver(measure);
19063
+ ro.observe(el);
19064
+ return () => ro.disconnect();
19065
+ }, [page.imageUrl]);
19066
+ React45.useEffect(() => {
19067
+ if (!selectedKey) return;
19068
+ if (!fields.some((f) => f.field_key === selectedKey && f.bbox)) return;
19069
+ const el = selectedBoxRef.current;
19070
+ if (el && typeof el.scrollIntoView === "function") {
19071
+ el.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
19072
+ }
19073
+ }, [selectedKey, fields, px]);
19074
+ const pagePts = { wPts: page.width_pt, hPts: page.height_pt };
19075
+ return /* @__PURE__ */ jsxs("div", { style: { position: "relative", width: "100%", lineHeight: 0 }, children: [
19076
+ page.imageUrl ? /* @__PURE__ */ jsx(
19077
+ "img",
19078
+ {
19079
+ ref: imgRef,
19080
+ src: page.imageUrl,
19081
+ alt: `Page ${page.page + 1}`,
19082
+ onLoad: () => {
19083
+ const el = imgRef.current;
19084
+ if (el && el.clientWidth && el.clientHeight) {
19085
+ setPx({ wPx: el.clientWidth, hPx: el.clientHeight });
19086
+ }
19087
+ },
19088
+ style: { display: "block", width: "100%", height: "auto" }
19089
+ }
19090
+ ) : (
19091
+ // No resolved image (host couldn't resolve the raster) — keep the
19092
+ // aspect box so cards still make sense, but show nothing to overlay on.
19093
+ /* @__PURE__ */ jsxs(
19094
+ "div",
19095
+ {
19096
+ style: {
19097
+ width: "100%",
19098
+ aspectRatio: `${page.width_pt} / ${page.height_pt}`,
19099
+ background: PAPER2,
19100
+ display: "flex",
19101
+ alignItems: "center",
19102
+ justifyContent: "center",
19103
+ fontSize: "12px",
19104
+ color: MUTED2,
19105
+ lineHeight: 1.4
19106
+ },
19107
+ children: [
19108
+ "Page ",
19109
+ page.page + 1,
19110
+ " image unavailable"
19111
+ ]
19112
+ }
19113
+ )
19114
+ ),
19115
+ px && page.imageUrl && fields.map((f) => {
19116
+ var _a;
19117
+ const rect = bboxToRect((_a = f.bbox) != null ? _a : null, pagePts, px);
19118
+ if (!rect) return null;
19119
+ const color = fieldColor2(f, secondary);
19120
+ const selected = selectedKey === f.field_key;
19121
+ return /* @__PURE__ */ jsx(
19122
+ "div",
19123
+ {
19124
+ ref: selected ? selectedBoxRef : null,
19125
+ "data-testid": "bbox-rect",
19126
+ "data-field-key": f.field_key,
19127
+ "data-selected": selected ? "true" : "false",
19128
+ onClick: () => onSelect(selected ? null : f.field_key),
19129
+ title: `${f.label}: ${f.value}`,
19130
+ style: {
19131
+ position: "absolute",
19132
+ left: `${rect.left}px`,
19133
+ top: `${rect.top}px`,
19134
+ width: `${rect.width}px`,
19135
+ height: `${rect.height}px`,
19136
+ border: `1px solid ${color}`,
19137
+ background: hexToRgba2(color, selected ? 0.28 : 0.12),
19138
+ boxShadow: selected ? `0 0 0 2px ${hexToRgba2(color, 0.5)}` : "none",
19139
+ borderRadius: "2px",
19140
+ cursor: "pointer",
19141
+ transition: "background 0.15s, box-shadow 0.15s"
19142
+ }
19143
+ },
19144
+ f.field_key
19145
+ );
19146
+ })
19147
+ ] });
19148
+ }
19149
+ function ExtractionDetail({
19150
+ pages,
19151
+ orderedFields,
19152
+ fieldsByPage,
19153
+ selectedKey,
19154
+ setSelectedKey,
19155
+ onFieldSave,
19156
+ correctionCounts,
19157
+ tab,
19158
+ setTab,
19159
+ editable,
19160
+ summary,
19161
+ fileName,
19162
+ pageCount,
19163
+ pagesProcessed,
19164
+ auditEvents,
19165
+ auditLoading
19166
+ }) {
19167
+ const { BORDER: BORDER4, MUTED: MUTED2, PAPER: PAPER2, SECONDARY: SECONDARY2 } = useTheme();
19168
+ const selectedCardRef = React45.useRef(null);
19169
+ React45.useEffect(() => {
19170
+ if (!selectedKey || tab !== "fields") return;
19171
+ const el = selectedCardRef.current;
19172
+ if (el && typeof el.scrollIntoView === "function") {
19173
+ el.scrollIntoView({ behavior: "smooth", block: "nearest" });
19174
+ }
19175
+ }, [selectedKey, tab]);
19176
+ const tabs = [
19177
+ { key: "details", label: "Details" },
19178
+ { key: "fields", label: "Fields", count: orderedFields.length }
19179
+ ];
19180
+ if (editable) tabs.push({ key: "audit", label: "Audit Trails", count: (auditEvents == null ? void 0 : auditEvents.length) || void 0 });
19181
+ return /* @__PURE__ */ jsxs(
19182
+ "div",
19183
+ {
19184
+ style: {
19185
+ display: "grid",
19186
+ gridTemplateColumns: "minmax(0, 3fr) minmax(260px, 1fr)",
19187
+ gap: "12px",
19188
+ height: "100%",
19189
+ minHeight: 0
19190
+ },
19191
+ children: [
19192
+ /* @__PURE__ */ jsx(
19193
+ "div",
19194
+ {
19195
+ style: {
19196
+ display: "flex",
19197
+ flexDirection: "column",
19198
+ gap: "10px",
19199
+ borderRadius: "0.75rem",
19200
+ border: `1px solid ${BORDER4}`,
19201
+ overflowY: "auto",
19202
+ minHeight: 0
19203
+ },
19204
+ children: pages.map((pg) => /* @__PURE__ */ jsx(
19205
+ PageOverlay,
19206
+ {
19207
+ page: pg,
19208
+ fields: fieldsByPage(pg.page),
19209
+ selectedKey,
19210
+ onSelect: (key) => {
19211
+ setSelectedKey(key);
19212
+ if (key) setTab("fields");
19213
+ },
19214
+ secondary: SECONDARY2
19215
+ },
19216
+ pg.page
19217
+ ))
19218
+ }
19219
+ ),
19220
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", minWidth: 0, minHeight: 0 }, children: [
19221
+ /* @__PURE__ */ jsx(
19222
+ "div",
19223
+ {
19224
+ style: {
19225
+ display: "flex",
19226
+ gap: "16px",
19227
+ borderBottom: `1px solid ${BORDER4}`,
19228
+ flexShrink: 0,
19229
+ marginBottom: "10px"
19230
+ },
19231
+ children: tabs.map((t) => {
19232
+ const active = tab === t.key;
19233
+ return /* @__PURE__ */ jsxs(
19234
+ "button",
19235
+ {
19236
+ type: "button",
19237
+ onClick: () => setTab(t.key),
19238
+ "data-testid": `tab-${t.key}`,
19239
+ style: {
19240
+ appearance: "none",
19241
+ background: "transparent",
19242
+ borderTop: "none",
19243
+ borderLeft: "none",
19244
+ borderRight: "none",
19245
+ borderBottom: `2px solid ${active ? SECONDARY2 : "transparent"}`,
19246
+ padding: "6px 2px",
19247
+ marginBottom: "-1px",
19248
+ cursor: "pointer",
19249
+ fontSize: "13px",
19250
+ fontWeight: 600,
19251
+ color: active ? SECONDARY2 : MUTED2,
19252
+ display: "inline-flex",
19253
+ alignItems: "center",
19254
+ gap: "6px"
19255
+ },
19256
+ children: [
19257
+ t.label,
19258
+ typeof t.count === "number" && /* @__PURE__ */ jsx(
19259
+ "span",
19260
+ {
19261
+ style: {
19262
+ fontSize: "11px",
19263
+ fontWeight: 600,
19264
+ padding: "1px 7px",
19265
+ borderRadius: "9999px",
19266
+ background: active ? "#eff6ff" : PAPER2,
19267
+ color: active ? SECONDARY2 : MUTED2
19268
+ },
19269
+ children: t.count
19270
+ }
19271
+ )
19272
+ ]
19273
+ },
19274
+ t.key
19275
+ );
19276
+ })
19277
+ }
19278
+ ),
19279
+ tab === "details" ? /* @__PURE__ */ jsx(
19280
+ FieldDetails,
19281
+ {
19282
+ summary,
19283
+ fileName,
19284
+ pageCount,
19285
+ pagesProcessed
19286
+ }
19287
+ ) : tab === "audit" ? /* @__PURE__ */ jsx("div", { style: { minHeight: 0, overflowY: "auto" }, children: /* @__PURE__ */ jsx(
19288
+ FieldAuditLog,
19289
+ {
19290
+ events: auditEvents != null ? auditEvents : [],
19291
+ loading: auditLoading != null ? auditLoading : false,
19292
+ fields: orderedFields.map((f) => ({ field_key: f.field_key, label: f.label }))
19293
+ }
19294
+ ) }) : /* @__PURE__ */ jsxs(
19295
+ "div",
19296
+ {
19297
+ style: {
19298
+ display: "flex",
19299
+ flexDirection: "column",
19300
+ minHeight: 0,
19301
+ overflowY: "auto",
19302
+ paddingRight: "4px"
19303
+ },
19304
+ children: [
19305
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: MUTED2, padding: "0 0 8px 2px", flexShrink: 0 }, children: [
19306
+ orderedFields.length,
19307
+ " extracted"
19308
+ ] }),
19309
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: "6px" }, children: orderedFields.map((f) => {
19310
+ var _a;
19311
+ const selected = selectedKey === f.field_key;
19312
+ return /* @__PURE__ */ jsx(
19313
+ FieldDetail,
19314
+ {
19315
+ field: f,
19316
+ selected,
19317
+ onSelect: setSelectedKey,
19318
+ onSave: onFieldSave,
19319
+ correctionCount: (_a = correctionCounts == null ? void 0 : correctionCounts[f.field_key]) != null ? _a : 0,
19320
+ cardRef: selected ? selectedCardRef : void 0
19321
+ },
19322
+ f.field_key
19323
+ );
19324
+ }) })
19325
+ ]
19326
+ }
19327
+ )
19328
+ ] })
19329
+ ]
19330
+ }
19331
+ );
19332
+ }
19333
+ function ExtractionModal({
19334
+ open,
19335
+ onClose,
19336
+ title,
19337
+ children
19338
+ }) {
19339
+ const { BORDER: BORDER4, MUTED: MUTED2 } = useTheme();
19340
+ React45.useEffect(() => {
19341
+ if (!open) return;
19342
+ const onKey = (e) => {
19343
+ if (e.key === "Escape") onClose();
19344
+ };
19345
+ window.addEventListener("keydown", onKey);
19346
+ const prev = document.body.style.overflow;
19347
+ document.body.style.overflow = "hidden";
19348
+ return () => {
19349
+ window.removeEventListener("keydown", onKey);
19350
+ document.body.style.overflow = prev;
19351
+ };
19352
+ }, [open, onClose]);
19353
+ if (!open || typeof document === "undefined") return null;
19354
+ return createPortal(
19355
+ /* @__PURE__ */ jsx(
19356
+ "div",
19357
+ {
19358
+ role: "dialog",
19359
+ "aria-modal": "true",
19360
+ "aria-label": title,
19361
+ onClick: onClose,
19362
+ style: {
19363
+ position: "fixed",
19364
+ inset: 0,
19365
+ zIndex: 2147483e3,
19366
+ background: "rgba(0,0,0,0.5)",
19367
+ display: "flex",
19368
+ alignItems: "center",
19369
+ justifyContent: "center",
19370
+ padding: "4vh 4vw",
19371
+ backdropFilter: "blur(2px)"
19372
+ },
19373
+ children: /* @__PURE__ */ jsxs(
19374
+ "div",
19375
+ {
19376
+ onClick: (e) => e.stopPropagation(),
19377
+ style: {
19378
+ width: "90vw",
19379
+ height: "90vh",
19380
+ background: "white",
19381
+ borderRadius: "0.875rem",
19382
+ border: `1px solid ${BORDER4}`,
19383
+ boxShadow: "0 20px 60px rgba(0,0,0,0.30)",
19384
+ display: "flex",
19385
+ flexDirection: "column",
19386
+ overflow: "hidden"
19387
+ },
19388
+ children: [
19389
+ /* @__PURE__ */ jsxs(
19390
+ "div",
19391
+ {
19392
+ style: {
19393
+ display: "flex",
19394
+ alignItems: "center",
19395
+ gap: "10px",
19396
+ padding: "11px 16px",
19397
+ borderBottom: `1px solid ${BORDER4}`,
19398
+ flexShrink: 0
19399
+ },
19400
+ children: [
19401
+ /* @__PURE__ */ jsx(
19402
+ "span",
19403
+ {
19404
+ style: {
19405
+ flex: 1,
19406
+ minWidth: 0,
19407
+ fontFamily: "var(--font-serif)",
19408
+ fontSize: "16px",
19409
+ fontWeight: 400,
19410
+ letterSpacing: "-0.01em",
19411
+ color: "var(--foreground)",
19412
+ overflow: "hidden",
19413
+ textOverflow: "ellipsis",
19414
+ whiteSpace: "nowrap"
19415
+ },
19416
+ children: title
19417
+ }
19418
+ ),
19419
+ /* @__PURE__ */ jsx(
19420
+ "button",
19421
+ {
19422
+ type: "button",
19423
+ "aria-label": "Close",
19424
+ onClick: onClose,
19425
+ style: {
19426
+ border: `1px solid ${BORDER4}`,
19427
+ borderRadius: "8px",
19428
+ background: "white",
19429
+ cursor: "pointer",
19430
+ padding: "5px 9px",
19431
+ fontSize: "13px",
19432
+ color: MUTED2,
19433
+ lineHeight: 1
19434
+ },
19435
+ children: "\u2715"
19436
+ }
19437
+ )
19438
+ ]
19439
+ }
19440
+ ),
19441
+ /* @__PURE__ */ jsx(
19442
+ "div",
19443
+ {
19444
+ style: {
19445
+ flex: 1,
19446
+ minHeight: 0,
19447
+ overflow: "hidden",
19448
+ display: "flex",
19449
+ flexDirection: "column",
19450
+ padding: "14px 16px"
19451
+ },
19452
+ children
19453
+ }
19454
+ )
19455
+ ]
19456
+ }
19457
+ )
19458
+ }
19459
+ ),
19460
+ document.body
19461
+ );
19462
+ }
19463
+ function DocumentFieldExtractionResolver(p) {
19464
+ var _a, _b;
19465
+ const { BORDER: BORDER4, MUTED: MUTED2, PAPER: PAPER2 } = useTheme();
19466
+ const [selectedKey, setSelectedKey] = React45.useState(null);
19467
+ const [open, setOpen] = React45.useState(false);
19468
+ const [tab, setTab] = React45.useState("fields");
19469
+ const pages = (_a = p.pages) != null ? _a : [];
19470
+ const fields = (_b = p.fields) != null ? _b : [];
19471
+ const editable = typeof p.onFieldSave === "function";
19472
+ const onLoadAudit = p.onLoadAudit;
19473
+ React45.useEffect(() => {
19474
+ if (tab === "audit" && onLoadAudit) {
19475
+ onLoadAudit();
19476
+ }
19477
+ }, [tab, onLoadAudit]);
19478
+ const correctionCounts = React45.useMemo(() => {
19479
+ var _a2, _b2;
19480
+ const counts = {};
19481
+ for (const ev of (_a2 = p.auditEvents) != null ? _a2 : []) {
19482
+ const human = ev.actor_type !== "system" && ev.event_type !== "ai_extracted";
19483
+ if (human) counts[ev.field_key] = ((_b2 = counts[ev.field_key]) != null ? _b2 : 0) + 1;
19484
+ }
19485
+ return counts;
19486
+ }, [p.auditEvents]);
19487
+ const orderedFields = [...fields].sort((a, b) => {
19488
+ var _a2, _b2;
19489
+ if (!!a.is_primary !== !!b.is_primary) return a.is_primary ? -1 : 1;
19490
+ return ((_a2 = b.confidence) != null ? _a2 : 0) - ((_b2 = a.confidence) != null ? _b2 : 0);
19491
+ });
19492
+ const fieldsByPage = (pageNo) => fields.filter((f) => f.page === pageNo);
19493
+ const csvCols = [
19494
+ { key: "label", label: "Field" },
19495
+ { key: "value", label: "Value" },
19496
+ { key: "page", label: "Page" },
19497
+ { key: "confidence", label: "Confidence" },
19498
+ { key: "badge", label: "Badge" },
19499
+ { key: "state", label: "State" }
19500
+ ];
19501
+ const csvRows = orderedFields.map((f) => {
19502
+ var _a2, _b2, _c;
19503
+ return {
19504
+ label: f.label,
19505
+ value: f.value,
19506
+ page: f.page + 1,
19507
+ confidence: (_a2 = f.confidence) != null ? _a2 : "",
19508
+ badge: (_b2 = f.badge) != null ? _b2 : "",
19509
+ state: (_c = f.state) != null ? _c : ""
19510
+ };
19511
+ });
19512
+ const title = p.file_name || (p.doc_type ? `${p.doc_type} \u2014 extracted fields` : "Extracted fields");
19513
+ const tierCounts = fields.reduce(
19514
+ (acc, f) => {
19515
+ var _a2;
19516
+ const t = (_a2 = f.confidence_tier) != null ? _a2 : "amber";
19517
+ if (t === "green") acc.high += 1;
19518
+ else if (t === "red") acc.low += 1;
19519
+ else acc.med += 1;
19520
+ return acc;
19521
+ },
19522
+ { high: 0, med: 0, low: 0 }
19523
+ );
19524
+ const chips = [
19525
+ { n: tierCounts.high, label: "high", color: TIER_COLORS2.green },
19526
+ { n: tierCounts.med, label: "medium", color: TIER_COLORS2.amber },
19527
+ { n: tierCounts.low, label: "low", color: TIER_COLORS2.red }
19528
+ ].filter((c) => c.n > 0);
19529
+ const thumb = pages.find((pg) => pg.imageUrl);
19530
+ return /* @__PURE__ */ jsxs(
19531
+ ComponentActions,
19532
+ {
19533
+ filename: "document-fields",
19534
+ onDownloadCSV: () => downloadCSV(csvCols, csvRows, "document-fields"),
19535
+ children: [
19536
+ /* @__PURE__ */ jsxs(
19537
+ "div",
19538
+ {
19539
+ role: "button",
19540
+ tabIndex: 0,
19541
+ onClick: () => setOpen(true),
19542
+ onKeyDown: (e) => {
19543
+ if (e.key === "Enter" || e.key === " ") {
19544
+ e.preventDefault();
19545
+ setOpen(true);
19546
+ }
19547
+ },
19548
+ style: {
19549
+ display: "flex",
19550
+ alignItems: "stretch",
19551
+ gap: "12px",
19552
+ padding: "12px",
19553
+ width: "100%",
19554
+ maxWidth: "440px",
19555
+ borderRadius: "0.75rem",
19556
+ border: `1px solid ${BORDER4}`,
19557
+ background: "white",
19558
+ cursor: "pointer",
19559
+ textAlign: "left",
19560
+ boxShadow: "0 1px 2px rgba(0,0,0,0.03), 0 4px 12px rgba(0,0,0,0.05)"
19561
+ },
19562
+ children: [
19563
+ /* @__PURE__ */ jsx(
19564
+ "div",
19565
+ {
19566
+ style: {
19567
+ width: "64px",
19568
+ flexShrink: 0,
19569
+ borderRadius: "0.5rem",
19570
+ border: `1px solid ${BORDER4}`,
19571
+ background: PAPER2,
19572
+ overflow: "hidden",
19573
+ display: "flex",
19574
+ alignItems: "center",
19575
+ justifyContent: "center"
19576
+ },
19577
+ children: (thumb == null ? void 0 : thumb.imageUrl) ? /* @__PURE__ */ jsx(
19578
+ "img",
19579
+ {
19580
+ src: thumb.imageUrl,
19581
+ alt: "Document preview",
19582
+ style: { width: "100%", height: "auto", display: "block" }
19583
+ }
19584
+ ) : /* @__PURE__ */ jsx("span", { style: { fontSize: "22px", lineHeight: 1, padding: "16px 0" }, children: "\u{1F4C4}" })
19585
+ }
19586
+ ),
19587
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: "6px" }, children: [
19588
+ /* @__PURE__ */ jsx(
19589
+ "p",
19590
+ {
19591
+ style: {
19592
+ fontFamily: "var(--font-serif)",
19593
+ fontSize: "15px",
19594
+ fontWeight: 400,
19595
+ color: "var(--foreground)",
19596
+ letterSpacing: "-0.01em",
19597
+ margin: 0,
19598
+ overflow: "hidden",
19599
+ textOverflow: "ellipsis",
19600
+ whiteSpace: "nowrap"
19601
+ },
19602
+ children: p.file_name || p.doc_type || "Document"
19603
+ }
19604
+ ),
19605
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: "12px", color: MUTED2 }, children: [
19606
+ fields.length,
19607
+ " field",
19608
+ fields.length === 1 ? "" : "s",
19609
+ " extracted \xB7 ",
19610
+ pages.length,
19611
+ " page",
19612
+ pages.length === 1 ? "" : "s"
19613
+ ] }),
19614
+ chips.length > 0 && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: "5px" }, children: chips.map((c) => /* @__PURE__ */ jsxs(
19615
+ "span",
19616
+ {
19617
+ style: {
19618
+ display: "inline-flex",
19619
+ alignItems: "center",
19620
+ gap: "4px",
19621
+ fontSize: "10px",
19622
+ fontWeight: 600,
19623
+ padding: "2px 7px",
19624
+ borderRadius: "9999px",
19625
+ background: hexToRgba2(c.color, 0.12),
19626
+ color: c.color
19627
+ },
19628
+ children: [
19629
+ /* @__PURE__ */ jsx(
19630
+ "span",
19631
+ {
19632
+ style: {
19633
+ width: "6px",
19634
+ height: "6px",
19635
+ borderRadius: "9999px",
19636
+ background: c.color
19637
+ }
19638
+ }
19639
+ ),
19640
+ c.n,
19641
+ " ",
19642
+ c.label
19643
+ ]
19644
+ },
19645
+ c.label
19646
+ )) }),
19647
+ /* @__PURE__ */ jsx(
19648
+ "span",
19649
+ {
19650
+ style: {
19651
+ marginTop: "2px",
19652
+ alignSelf: "flex-start",
19653
+ fontSize: "12px",
19654
+ fontWeight: 600,
19655
+ padding: "6px 12px",
19656
+ borderRadius: "0.5rem",
19657
+ background: "var(--foreground)",
19658
+ color: "white"
19659
+ },
19660
+ children: "View & edit fields"
19661
+ }
19662
+ )
19663
+ ] })
19664
+ ]
19665
+ }
19666
+ ),
19667
+ /* @__PURE__ */ jsx(ExtractionModal, { open, onClose: () => setOpen(false), title, children: /* @__PURE__ */ jsx(
19668
+ ExtractionDetail,
19669
+ {
19670
+ pages,
19671
+ orderedFields,
19672
+ fieldsByPage,
19673
+ selectedKey,
19674
+ setSelectedKey,
19675
+ onFieldSave: p.onFieldSave,
19676
+ correctionCounts,
19677
+ tab,
19678
+ setTab,
19679
+ editable,
19680
+ summary: p.summary,
19681
+ fileName: p.file_name,
19682
+ pageCount: p.page_count,
19683
+ pagesProcessed: p.pages_processed,
19684
+ auditEvents: p.auditEvents,
19685
+ auditLoading: p.auditLoading
19686
+ }
19687
+ ) })
19688
+ ]
19689
+ }
19690
+ );
19691
+ }
19692
+ init_ThemeContext();
17959
19693
  var SEVERITY_COLORS = {
17960
19694
  high: { color: "#dc2626", bg: "#fef2f2" },
17961
19695
  medium: { color: "#f59e0b", bg: "#fff7ed" },
@@ -17986,8 +19720,8 @@ function formatAmount2(amount, currency = "USD") {
17986
19720
  return `${sign}${symbol}${body}`;
17987
19721
  }
17988
19722
  function useContainerWidth2(ref) {
17989
- const [w, setW] = React41.useState(0);
17990
- React41.useLayoutEffect(() => {
19723
+ const [w, setW] = React45.useState(0);
19724
+ React45.useLayoutEffect(() => {
17991
19725
  const el = ref.current;
17992
19726
  if (!el || typeof ResizeObserver === "undefined") return;
17993
19727
  const ro = new ResizeObserver((entries) => {
@@ -18059,7 +19793,7 @@ function DecisionCardRenderer({
18059
19793
  }) {
18060
19794
  var _a, _b;
18061
19795
  const { BORDER: BORDER4, MUTED: MUTED2, ACCENT: ACCENT2, ACCENT_SOFT: ACCENT_SOFT2 } = useTheme();
18062
- const rootRef = React41.useRef(null);
19796
+ const rootRef = React45.useRef(null);
18063
19797
  const width = useContainerWidth2(rootRef);
18064
19798
  const stacked = width > 0 && width < STACK_BELOW;
18065
19799
  const options = (_a = data.options) != null ? _a : [];
@@ -18316,8 +20050,8 @@ function DecisionCardResolver(p) {
18316
20050
  const decisionId = (_a = p.id) != null ? _a : p.title;
18317
20051
  const hostResolved = resolvedDecisions == null ? void 0 : resolvedDecisions[decisionId];
18318
20052
  const initial = (_c = hostResolved != null ? hostResolved : (_b = p.resolved) == null ? void 0 : _b.option) != null ? _c : null;
18319
- const [localResolved, setLocalResolved] = React41.useState(initial);
18320
- React41.useEffect(() => {
20053
+ const [localResolved, setLocalResolved] = React45.useState(initial);
20054
+ React45.useEffect(() => {
18321
20055
  if (hostResolved !== void 0) setLocalResolved(hostResolved);
18322
20056
  }, [hostResolved]);
18323
20057
  const resolved = hostResolved != null ? hostResolved : localResolved;
@@ -18459,6 +20193,8 @@ function resolveUI(rawPayload) {
18459
20193
  return /* @__PURE__ */ jsx(PipelinePreviewResolver, __spreadValues({}, payload));
18460
20194
  case "workflow-stepper":
18461
20195
  return /* @__PURE__ */ jsx(WorkflowStepperResolver, __spreadValues({}, payload));
20196
+ case "document-field-extraction":
20197
+ return /* @__PURE__ */ jsx(DocumentFieldExtractionResolver, __spreadValues({}, payload));
18462
20198
  case "decision-card":
18463
20199
  return /* @__PURE__ */ jsx(DecisionCardResolver, __spreadValues({}, payload));
18464
20200
  default: {