@kontourai/survey 1.10.0 → 1.12.0

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.
@@ -41,6 +41,7 @@ export declare const publicDirectoryReviewItemExample: {
41
41
  confidence: number;
42
42
  extractor: string;
43
43
  extractedAt: string;
44
+ model?: undefined;
44
45
  };
45
46
  claimTarget: {
46
47
  claimId: string;
@@ -92,6 +93,7 @@ export declare const publicDirectoryReviewItemExample: {
92
93
  target: string;
93
94
  confidence: number;
94
95
  extractor: string;
96
+ model: string;
95
97
  extractedAt: string;
96
98
  };
97
99
  claimTarget: {
@@ -467,6 +469,7 @@ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
467
469
  confidence: number;
468
470
  extractor: string;
469
471
  extractedAt: string;
472
+ model?: undefined;
470
473
  };
471
474
  claimTarget: {
472
475
  claimId: string;
@@ -518,6 +521,7 @@ export declare const reviewWorkbenchQueueExamples: (ReviewItem | {
518
521
  target: string;
519
522
  confidence: number;
520
523
  extractor: string;
524
+ model: string;
521
525
  extractedAt: string;
522
526
  };
523
527
  claimTarget: {
@@ -94,6 +94,7 @@ export const publicDirectoryReviewItemExample = {
94
94
  target: "availabilityStatus",
95
95
  confidence: 0.82,
96
96
  extractor: "example-crawl",
97
+ model: "example-extraction-model-2026-05",
97
98
  extractedAt: "2026-05-31T15:00:00.000Z",
98
99
  },
99
100
  claimTarget: {
@@ -481,9 +482,180 @@ function queueFixture(name, target, currentValue, proposedValue, candidateSetSta
481
482
  },
482
483
  };
483
484
  }
485
+ /**
486
+ * A field with a proposed value but no current candidate at all — the "New" field
487
+ * kind in the review workbench (no "Current" value to diff against, so the diff
488
+ * shows "Not set" and the Keep-current button reads "Leave unset").
489
+ */
490
+ const dropInPriceReviewItemExample = {
491
+ apiVersion: reviewResourceApiVersion,
492
+ kind: "ReviewItem",
493
+ metadata: {
494
+ name: "public-directory-dropin-price",
495
+ labels: { domain: "public-directory" },
496
+ producer: { displayName: "Example Program", slug: "example-program" },
497
+ },
498
+ spec: {
499
+ target: "dropInPrice",
500
+ candidateSetStatus: "needs-review",
501
+ producerPolicy: { feedbackTags: ["pricing-field", "crawler-suggested"] },
502
+ rationale: "Example-backed local review queue item for dropInPrice.",
503
+ candidates: [
504
+ {
505
+ id: "public-directory-dropin-price:candidate:proposed",
506
+ role: "proposed",
507
+ value: "$85 / day",
508
+ confidence: 0.72,
509
+ source: {
510
+ sourceId: "public-directory-dropin-price:source:proposed",
511
+ sourceRef: "https://example.test/listings/example-program/pricing",
512
+ kind: "web-page",
513
+ observedAt: "2026-05-31T15:00:00.000Z",
514
+ locatorScheme: "html",
515
+ },
516
+ locator: {
517
+ scheme: "html",
518
+ locator: "html:field=dropInPrice",
519
+ excerpt: "Daily drop-in rate: $85 (members $70). Full-week enrollment preferred.",
520
+ },
521
+ extraction: {
522
+ extractionId: "public-directory-dropin-price:extraction:proposed",
523
+ target: "dropInPrice",
524
+ confidence: 0.72,
525
+ extractor: "example-crawl",
526
+ extractedAt: "2026-05-31T15:00:00.000Z",
527
+ },
528
+ claimTarget: {
529
+ claimId: "public-field.entity-123.dropin-price.proposal-457",
530
+ subjectType: "public-record.entity",
531
+ subjectId: "entity-123",
532
+ facet: "public-record.profile",
533
+ claimType: "public-data.field-candidate",
534
+ fieldOrBehavior: "dropInPrice",
535
+ impactLevel: "low",
536
+ collectedBy: "example-crawl",
537
+ },
538
+ projection: {
539
+ rawSourceId: "public-directory-dropin-price:source:proposed",
540
+ extractionId: "public-directory-dropin-price:extraction:proposed",
541
+ candidateSetId: "public-directory-dropin-price:candidate-set",
542
+ candidateId: "public-directory-dropin-price:candidate:proposed",
543
+ claimId: "public-field.entity-123.dropin-price.proposal-457",
544
+ },
545
+ producer: { proposalId: "proposal-457" },
546
+ },
547
+ ],
548
+ },
549
+ status: { observedCandidateCount: 1 },
550
+ };
551
+ /**
552
+ * A field whose proposed value has NO supporting excerpt/locator — the review
553
+ * workbench must show the explicit "No source — verify before accepting" flag
554
+ * instead of a confidence meter or a weak "none" placeholder.
555
+ */
556
+ const dailyHoursNoSourceReviewItemExample = {
557
+ apiVersion: reviewResourceApiVersion,
558
+ kind: "ReviewItem",
559
+ metadata: {
560
+ name: "public-directory-daily-hours-no-source",
561
+ labels: { domain: "public-directory" },
562
+ producer: { displayName: "Example Program", slug: "example-program" },
563
+ },
564
+ spec: {
565
+ target: "dailyHours",
566
+ candidateSetStatus: "needs-review",
567
+ producerPolicy: { feedbackTags: ["hours-change", "unsourced-proposal"] },
568
+ rationale: "Example-backed local review queue item for dailyHours with no provenance.",
569
+ candidates: [
570
+ {
571
+ id: "public-directory-daily-hours-no-source:candidate:current",
572
+ role: "current",
573
+ value: "9:00 AM - 3:00 PM",
574
+ confidence: 0.9,
575
+ source: {
576
+ sourceId: "public-directory-daily-hours-no-source:source:current",
577
+ sourceRef: "https://example.test/listings/example-program",
578
+ kind: "web-page",
579
+ observedAt: "2026-05-30T18:00:00.000Z",
580
+ locatorScheme: "html",
581
+ },
582
+ locator: {
583
+ scheme: "html",
584
+ locator: "html:field=dailyHours",
585
+ excerpt: "Daily program hours are 9:00 AM to 3:00 PM.",
586
+ },
587
+ extraction: {
588
+ extractionId: "public-directory-daily-hours-no-source:extraction:current",
589
+ target: "dailyHours",
590
+ confidence: 0.9,
591
+ extractor: "example-field-review",
592
+ extractedAt: "2026-05-30T18:00:00.000Z",
593
+ },
594
+ claimTarget: {
595
+ claimId: "public-field.entity-123.daily-hours.current",
596
+ subjectType: "public-record.entity",
597
+ subjectId: "entity-123",
598
+ facet: "public-record.profile",
599
+ claimType: "public-data.field",
600
+ fieldOrBehavior: "dailyHours",
601
+ impactLevel: "medium",
602
+ collectedBy: "example-field-review",
603
+ },
604
+ projection: {
605
+ rawSourceId: "public-directory-daily-hours-no-source:source:current",
606
+ extractionId: "public-directory-daily-hours-no-source:extraction:current",
607
+ candidateSetId: "public-directory-daily-hours-no-source:candidate-set",
608
+ candidateId: "public-directory-daily-hours-no-source:candidate:current",
609
+ claimId: "public-field.entity-123.daily-hours.current",
610
+ },
611
+ },
612
+ {
613
+ // No `locator` at all — the proposed value has no supporting excerpt.
614
+ id: "public-directory-daily-hours-no-source:candidate:proposed",
615
+ role: "proposed",
616
+ value: "8:30 AM - 4:00 PM",
617
+ confidence: 0.68,
618
+ source: {
619
+ sourceId: "public-directory-daily-hours-no-source:source:proposed",
620
+ sourceRef: "manual-entry://example-program/daily-hours-update",
621
+ kind: "manual-entry",
622
+ observedAt: "2026-05-31T15:00:00.000Z",
623
+ },
624
+ extraction: {
625
+ extractionId: "public-directory-daily-hours-no-source:extraction:proposed",
626
+ target: "dailyHours",
627
+ confidence: 0.68,
628
+ extractor: "example-crawl",
629
+ extractedAt: "2026-05-31T15:00:00.000Z",
630
+ },
631
+ claimTarget: {
632
+ claimId: "public-field.entity-123.daily-hours.proposal-458",
633
+ subjectType: "public-record.entity",
634
+ subjectId: "entity-123",
635
+ facet: "public-record.profile",
636
+ claimType: "public-data.field-candidate",
637
+ fieldOrBehavior: "dailyHours",
638
+ impactLevel: "medium",
639
+ collectedBy: "example-crawl",
640
+ },
641
+ projection: {
642
+ rawSourceId: "public-directory-daily-hours-no-source:source:proposed",
643
+ extractionId: "public-directory-daily-hours-no-source:extraction:proposed",
644
+ candidateSetId: "public-directory-daily-hours-no-source:candidate-set",
645
+ candidateId: "public-directory-daily-hours-no-source:candidate:proposed",
646
+ claimId: "public-field.entity-123.daily-hours.proposal-458",
647
+ },
648
+ producer: { proposalId: "proposal-458" },
649
+ },
650
+ ],
651
+ },
652
+ status: { observedCandidateCount: 2 },
653
+ };
484
654
  export const reviewWorkbenchQueueExamples = [
485
655
  queueFixture("public-directory-hours", "hours", "Weekdays 9am-5pm", "Weekdays 8am-6pm", "needs-review", ["hours-change", "crawler-suggested"]),
486
656
  queueFixture("public-directory-phone", "phoneNumber", "+1-555-0100", "+1-555-0199", "needs-review", ["contact-field", "source-conflict"]),
657
+ dropInPriceReviewItemExample,
658
+ dailyHoursNoSourceReviewItemExample,
487
659
  publicDirectoryReviewItemExample,
488
660
  queueFixture("public-directory-address", "streetAddress", "100 Main Street", "102 Main Street", "needs-review", ["address-change", "producer-escalation-candidate"]),
489
661
  queueFixture("public-directory-license", "licenseStatus", "ACTIVE", "EXPIRED", "escalated", ["licensing", "manual-review-required"]),
@@ -24,7 +24,11 @@
24
24
  * host-page rules always win over the shadow defaults.
25
25
  *
26
26
  * Attributes:
27
- * theme — "survey" | "console" | "flow" | "surface" (maps to theme-* class)
27
+ * theme — "survey" | "console" | "flow" | "surface" (maps to a theme-* class
28
+ * with its own brand colour) | "custom" (or any other value: no
29
+ * theme-* class at all, so a host's own --k-* overrides apply with
30
+ * nothing to fight — see the "Theming" section of
31
+ * docs/consumer-integration-guide.md for a full host-brand example)
28
32
  * color-scheme — "dark" | "light" (default: "dark")
29
33
  * src — URL to fetch a JSON-serialised ReviewQueueSessionState
30
34
  */
@@ -24,7 +24,11 @@
24
24
  * host-page rules always win over the shadow defaults.
25
25
  *
26
26
  * Attributes:
27
- * theme — "survey" | "console" | "flow" | "surface" (maps to theme-* class)
27
+ * theme — "survey" | "console" | "flow" | "surface" (maps to a theme-* class
28
+ * with its own brand colour) | "custom" (or any other value: no
29
+ * theme-* class at all, so a host's own --k-* overrides apply with
30
+ * nothing to fight — see the "Theming" section of
31
+ * docs/consumer-integration-guide.md for a full host-brand example)
28
32
  * color-scheme — "dark" | "light" (default: "dark")
29
33
  * src — URL to fetch a JSON-serialised ReviewQueueSessionState
30
34
  */
@@ -61,20 +65,41 @@ const TOKEN_INHERIT_CSS = `/* 1. Host token defaults — literal values, not var
61
65
  --k-line: rgba(255,255,255,0.08);
62
66
  --k-line-strong: rgba(255,255,255,0.14);
63
67
  --k-brand: #5ce0c6;
68
+ --k-brand-contrast: #06080b;
64
69
  --k-active: #7aa2ff;
65
70
  --k-positive: #34d399;
66
71
  --k-caution: #f3b14b;
67
72
  --k-negative: #ff6f6f;
68
73
  --k-neutral: #8b93a8;
74
+ --k-positive-soft: rgba(52,211,153,0.14);
75
+ --k-caution-soft: rgba(243,177,75,0.14);
76
+ --k-negative-soft: rgba(255,111,111,0.14);
77
+ --k-active-soft: rgba(122,162,255,0.14);
69
78
  --k-radius-md: 10px;
70
79
  --k-radius-sm: 6px;
80
+ --k-shadow: 0 26px 60px -42px rgba(0, 0, 0, 0.95);
71
81
  --k-font-ui: "Hanken Grotesk", system-ui, sans-serif;
82
+ /* Field-diff card aliases (Theming section of docs/consumer-integration-guide.md).
83
+ Derived from the tokens above by default; a host may override any of these
84
+ directly for finer control without touching the base token it derives from. */
85
+ --k-muted: var(--k-text-muted);
86
+ --k-faint: var(--k-text-faint);
87
+ --k-raised: var(--k-panel-raised);
88
+ --k-sunken: color-mix(in srgb, var(--k-bg) 55%, var(--k-panel) 45%);
89
+ --k-brand-ink: var(--k-brand-contrast);
90
+ --k-brand-wash: color-mix(in srgb, var(--k-brand) 14%, transparent);
91
+ --k-positive-wash: var(--k-positive-soft);
92
+ --k-caution-wash: var(--k-caution-soft);
93
+ --k-negative-wash: var(--k-negative-soft);
94
+ --k-radius: var(--k-radius-md);
72
95
  }
73
96
  /* 2. Token inheritance delegation — re-delegate --k-* tokens on the embed root
74
97
  to inherit from :host, so external overrides set on the host element propagate
75
98
  through the shadow boundary. The [class] attribute selector raises specificity
76
99
  to (0,2,0) — matching the theme class selectors — and this sheet comes AFTER
77
- the workbench defaults, so source order makes these rules win. */
100
+ the workbench defaults, so source order makes these rules win. Every token the
101
+ workbench CSS reads is listed here so a host can override ANY of them (the full
102
+ --k-* set, not just a fixed subset) without forking styles or picking a preset. */
78
103
  .survey-workbench-embed[class] {
79
104
  --k-bg: inherit;
80
105
  --k-panel: inherit;
@@ -85,16 +110,32 @@ const TOKEN_INHERIT_CSS = `/* 1. Host token defaults — literal values, not var
85
110
  --k-line: inherit;
86
111
  --k-line-strong: inherit;
87
112
  --k-brand: inherit;
113
+ --k-brand-contrast: inherit;
88
114
  --k-active: inherit;
89
115
  --k-positive: inherit;
90
116
  --k-caution: inherit;
91
117
  --k-negative: inherit;
92
118
  --k-neutral: inherit;
119
+ --k-positive-soft: inherit;
120
+ --k-caution-soft: inherit;
121
+ --k-negative-soft: inherit;
122
+ --k-active-soft: inherit;
93
123
  --k-radius-md: inherit;
94
124
  --k-radius-sm: inherit;
125
+ --k-shadow: inherit;
95
126
  --k-font-ui: inherit;
96
127
  --k-font-mono: inherit;
97
128
  --k-font-display: inherit;
129
+ --k-muted: inherit;
130
+ --k-faint: inherit;
131
+ --k-raised: inherit;
132
+ --k-sunken: inherit;
133
+ --k-brand-ink: inherit;
134
+ --k-brand-wash: inherit;
135
+ --k-positive-wash: inherit;
136
+ --k-caution-wash: inherit;
137
+ --k-negative-wash: inherit;
138
+ --k-radius: inherit;
98
139
  }
99
140
  /* 3. Light mode token overrides — applied when color-scheme="light" sets data-theme="light"
100
141
  on the embed root. These are literal values (not var()) to avoid self-reference cycles.
@@ -112,11 +153,16 @@ const TOKEN_INHERIT_CSS = `/* 1. Host token defaults — literal values, not var
112
153
  --k-text-muted: #5b626b;
113
154
  --k-text-faint: #707782;
114
155
  --k-brand: #16806f;
156
+ --k-brand-contrast: #ffffff;
115
157
  --k-positive: #168257;
116
158
  --k-caution: #8a5a00;
117
159
  --k-negative: #c83b3b;
118
160
  --k-active: #3f6fd6;
119
161
  }`;
162
+ /** The four built-in theme presets from vendor kontourai-ui/tokens/themes.css. Any
163
+ * other `theme` attribute value (including the documented `"custom"`) opts out of
164
+ * presets entirely — see {@link SurveyReviewWorkbenchElement.#applyThemeClasses}. */
165
+ const KNOWN_WORKBENCH_THEMES = new Set(["survey", "console", "flow", "surface"]);
120
166
  export class SurveyReviewWorkbenchElement extends HTMLElement {
121
167
  static observedAttributes = ["theme", "color-scheme", "src"];
122
168
  #session = null;
@@ -157,13 +203,28 @@ export class SurveyReviewWorkbenchElement extends HTMLElement {
157
203
  --k-line: rgba(255,255,255,0.08);
158
204
  --k-line-strong: rgba(255,255,255,0.14);
159
205
  --k-brand: #5ce0c6;
206
+ --k-brand-contrast: #06080b;
160
207
  --k-active: #7aa2ff;
161
208
  --k-positive: #34d399;
162
209
  --k-caution: #f3b14b;
163
210
  --k-negative: #ff6f6f;
211
+ --k-positive-soft: rgba(52,211,153,0.14);
212
+ --k-caution-soft: rgba(243,177,75,0.14);
213
+ --k-negative-soft: rgba(255,111,111,0.14);
164
214
  --k-radius-md: 10px;
165
215
  --k-radius-sm: 6px;
216
+ --k-shadow: 0 26px 60px -42px rgba(0, 0, 0, 0.95);
166
217
  --k-font-ui: "Hanken Grotesk", system-ui, sans-serif;
218
+ --k-muted: var(--k-text-muted);
219
+ --k-faint: var(--k-text-faint);
220
+ --k-raised: var(--k-panel-raised);
221
+ --k-sunken: color-mix(in srgb, var(--k-bg) 55%, var(--k-panel) 45%);
222
+ --k-brand-ink: var(--k-brand-contrast);
223
+ --k-brand-wash: color-mix(in srgb, var(--k-brand) 14%, transparent);
224
+ --k-positive-wash: var(--k-positive-soft);
225
+ --k-caution-wash: var(--k-caution-soft);
226
+ --k-negative-wash: var(--k-negative-soft);
227
+ --k-radius: var(--k-radius-md);
167
228
  }
168
229
  .workbench-empty, .workbench-error {
169
230
  display: flex;
@@ -194,15 +255,30 @@ export class SurveyReviewWorkbenchElement extends HTMLElement {
194
255
  --k-line: inherit;
195
256
  --k-line-strong: inherit;
196
257
  --k-brand: inherit;
258
+ --k-brand-contrast: inherit;
197
259
  --k-active: inherit;
198
260
  --k-positive: inherit;
199
261
  --k-caution: inherit;
200
262
  --k-negative: inherit;
263
+ --k-positive-soft: inherit;
264
+ --k-caution-soft: inherit;
265
+ --k-negative-soft: inherit;
201
266
  --k-radius-md: inherit;
202
267
  --k-radius-sm: inherit;
268
+ --k-shadow: inherit;
203
269
  --k-font-ui: inherit;
204
270
  --k-font-mono: inherit;
205
271
  --k-font-display: inherit;
272
+ --k-muted: inherit;
273
+ --k-faint: inherit;
274
+ --k-raised: inherit;
275
+ --k-sunken: inherit;
276
+ --k-brand-ink: inherit;
277
+ --k-brand-wash: inherit;
278
+ --k-positive-wash: inherit;
279
+ --k-caution-wash: inherit;
280
+ --k-negative-wash: inherit;
281
+ --k-radius: inherit;
206
282
  }
207
283
  `;
208
284
  this.#root.appendChild(hostStyle);
@@ -266,7 +342,14 @@ export class SurveyReviewWorkbenchElement extends HTMLElement {
266
342
  #applyThemeClasses() {
267
343
  const theme = this.getAttribute("theme") ?? "survey";
268
344
  const colorScheme = this.getAttribute("color-scheme") ?? "dark";
269
- this.#mountRoot.className = `workbench survey-workbench-embed theme-${theme}`;
345
+ // Only the four built-in presets get a `theme-*` class (see vendor
346
+ // kontourai-ui/tokens/themes.css). `theme="custom"` — or any other value that
347
+ // isn't one of the four presets — deliberately gets NO theme class, so none of
348
+ // the presets' brand-colour overrides apply. This is the escape hatch for a
349
+ // host that wants to set its own full `--k-*` palette (see the "Theming"
350
+ // section of docs/consumer-integration-guide.md) without fighting a preset.
351
+ const themeClass = KNOWN_WORKBENCH_THEMES.has(theme) ? ` theme-${theme}` : "";
352
+ this.#mountRoot.className = `workbench survey-workbench-embed${themeClass}`;
270
353
  this.#mountRoot.setAttribute("data-color-scheme", colorScheme);
271
354
  // data-theme="light" is required by the token sheet for light mode token overrides.
272
355
  // data-dark is kept for any existing selectors that used it.