@thehammer/template-verification 0.1.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.
- package/README.md +112 -0
- package/dist/components/ComparisonPage.vue.d.ts +2 -0
- package/dist/components/ConfidenceIcon.vue.d.ts +14 -0
- package/dist/components/VerificationChrome.vue.d.ts +2 -0
- package/dist/components/VerificationModal.vue.d.ts +25 -0
- package/dist/components/VerifiedField.vue.d.ts +30 -0
- package/dist/constants.d.ts +10 -0
- package/dist/context.d.ts +68 -0
- package/dist/index.d.ts +24 -0
- package/dist/lib/confidence.d.ts +19 -0
- package/dist/lib/discrepancy.d.ts +14 -0
- package/dist/lib/resolveDataPoint.d.ts +29 -0
- package/dist/lib/sources.d.ts +18 -0
- package/dist/lib/walk.d.ts +24 -0
- package/dist/style.css +1 -0
- package/dist/template-verification.js +648 -0
- package/dist/types.d.ts +98 -0
- package/dist/useSaveDataPoint.d.ts +17 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# @thehammer/template-verification
|
|
2
|
+
|
|
3
|
+
A read-write **verification overlay** for rendered template apps. It overlays a
|
|
4
|
+
trust layer on top of an already-rendered document template: per-data-point
|
|
5
|
+
confidence/status icons, discrepancy indicators, a click-through detail modal,
|
|
6
|
+
inline editing, and a recursive comparison page.
|
|
7
|
+
|
|
8
|
+
The library **fetches nothing** — the host passes in the rendered `data` (with an
|
|
9
|
+
embedded `__meta` sidecar) and a `saveOverride` transport via a single
|
|
10
|
+
`initVerification()` call. It is backend-agnostic and unit-testable standalone.
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install @thehammer/template-verification
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
`vue` and `@thehammer/danx-ui` are peer dependencies (runtime externals) — the
|
|
19
|
+
host template app already loads both.
|
|
20
|
+
|
|
21
|
+
Import the stylesheet once (it carries the `@media print` rule that hides the
|
|
22
|
+
overlay chrome on print/export):
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import "@thehammer/template-verification/style.css"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
Call `initVerification()` **once** at your App root (a function call — not a
|
|
31
|
+
wrapping element), then drop a single `<VerificationChrome/>` as a sibling of the
|
|
32
|
+
document and use `<VerifiedField>` anywhere:
|
|
33
|
+
|
|
34
|
+
```vue
|
|
35
|
+
<script setup lang="ts">
|
|
36
|
+
import { initVerification, VerificationChrome, VerifiedField } from "@thehammer/template-verification"
|
|
37
|
+
|
|
38
|
+
const props = defineProps<{ data: any }>()
|
|
39
|
+
|
|
40
|
+
// Host-supplied, authenticated transport. Its presence enables inline editing.
|
|
41
|
+
async function saveOverride(args) {
|
|
42
|
+
const res = await fetch(`/api/workflow-inputs/${args.workflow_input_id}/data-point-overrides`, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
45
|
+
body: JSON.stringify({ ...args, object_id: args.object_ids.at(-1) })
|
|
46
|
+
})
|
|
47
|
+
return res.json() // recomputed DataPointMeta
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Once, at root setup. Absent saveOverride ⇒ every field is read-only.
|
|
51
|
+
initVerification({
|
|
52
|
+
data: props.data,
|
|
53
|
+
saveOverride,
|
|
54
|
+
workflowInputId: props.data.workflow_input_id
|
|
55
|
+
})
|
|
56
|
+
</script>
|
|
57
|
+
|
|
58
|
+
<template>
|
|
59
|
+
<article>
|
|
60
|
+
<h1><VerifiedField :source="data.claimant" field="full_name" /></h1>
|
|
61
|
+
<div v-for="(p, i) in data.medical_providers" :key="i">
|
|
62
|
+
<VerifiedField :source="p" field="name" />
|
|
63
|
+
</div>
|
|
64
|
+
</article>
|
|
65
|
+
|
|
66
|
+
<!-- Sibling of the document, placed once. -->
|
|
67
|
+
<VerificationChrome />
|
|
68
|
+
</template>
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The backing store is a **module singleton** — safe because each rendered template
|
|
72
|
+
runs as its own isolated Vue app inside its own iframe (one JS module instance,
|
|
73
|
+
one app-wide config, no SSR). See `src/context.ts` for the invariant.
|
|
74
|
+
|
|
75
|
+
## The `__meta` contract
|
|
76
|
+
|
|
77
|
+
Each rendered object node carries a `__meta` map keyed by leaf field name. Each
|
|
78
|
+
entry is a `DataPointMeta`:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
interface DataPointMeta {
|
|
82
|
+
anchor: { object_ids: (number | string)[]; field: string; field_path: string }
|
|
83
|
+
candidates: {
|
|
84
|
+
override?: { value; source_choice }
|
|
85
|
+
extracted?: { value; confidence: number | null; reasoning; cited_text; cited_text_verified; sources[] }
|
|
86
|
+
claim?: { value; claim_set_label }
|
|
87
|
+
}
|
|
88
|
+
discrepancy: boolean
|
|
89
|
+
resolved: { value; source: "override" | "extracted" | "claim" | "none" }
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Resolution + status
|
|
94
|
+
|
|
95
|
+
- **Resolution priority:** `override > extracted > claim`.
|
|
96
|
+
- **Status** buckets the **integer** confidence: `null → unverifiable (gray)`,
|
|
97
|
+
`≤2 → low (red)`, `==3 → medium (yellow)`, `≥4 → high (green)`.
|
|
98
|
+
- **Discrepancy** is orthogonal to confidence — a high-confidence field can still
|
|
99
|
+
disagree with a differing claim/override.
|
|
100
|
+
|
|
101
|
+
## Activation
|
|
102
|
+
|
|
103
|
+
Fully self-contained: a fixed bottom-right settings button toggles the overlay,
|
|
104
|
+
persisted to `localStorage`. No URL flags, no postMessage, no host activation prop.
|
|
105
|
+
All library chrome is hidden under print/export media.
|
|
106
|
+
|
|
107
|
+
## Develop
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
npm test # vitest
|
|
111
|
+
npm run build # vite lib build + emitted .d.ts types
|
|
112
|
+
```
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { VerificationStatus } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Pure presentational confidence/status glyph. Given either a raw integer
|
|
4
|
+
* confidence (1-5) or an explicit status, renders a colored glyph and optional
|
|
5
|
+
* label. Buckets the INTEGER — never string-matches the confidence (see
|
|
6
|
+
* {@link bucketConfidence}). No interactivity.
|
|
7
|
+
*/
|
|
8
|
+
type __VLS_Props = {
|
|
9
|
+
confidence?: number | null;
|
|
10
|
+
status?: VerificationStatus;
|
|
11
|
+
showLabel?: boolean;
|
|
12
|
+
};
|
|
13
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
declare const _default: import("vue").DefineComponent<{}, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
export default _default;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { DataPointMeta, SourceChoice } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Per-data-point detail dialog (danx-ui {@link DanxDialog}, not hand-rolled).
|
|
4
|
+
* Shows the resolved value + winning source, every candidate side-by-side with
|
|
5
|
+
* any discrepancy called out, the extracted candidate's confidence / reasoning /
|
|
6
|
+
* cited text (+ verification flag) / grouped source pages, and a resolution
|
|
7
|
+
* control whose three actions create or update the override via the injected save.
|
|
8
|
+
*
|
|
9
|
+
* `save` is null when the field is read-only (no transport / no anchor) — the
|
|
10
|
+
* resolution control is hidden in that case.
|
|
11
|
+
*/
|
|
12
|
+
type __VLS_Props = {
|
|
13
|
+
modelValue: boolean;
|
|
14
|
+
meta: DataPointMeta;
|
|
15
|
+
label?: string;
|
|
16
|
+
save?: ((value: string | null, sourceChoice: SourceChoice) => Promise<DataPointMeta>) | null;
|
|
17
|
+
};
|
|
18
|
+
declare const _default: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
|
|
19
|
+
"update:modelValue": (value: boolean) => any;
|
|
20
|
+
saved: (meta: DataPointMeta) => any;
|
|
21
|
+
}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
|
|
22
|
+
"onUpdate:modelValue"?: ((value: boolean) => any) | undefined;
|
|
23
|
+
onSaved?: ((meta: DataPointMeta) => any) | undefined;
|
|
24
|
+
}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { MetaCarrier } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a single rendered value. Reads the data point's `__meta` from the bound
|
|
4
|
+
* `source` object at `field` (`source.__meta[field]`), renders the RESOLVED value
|
|
5
|
+
* (override > extracted > claim), and — when the overlay is enabled via
|
|
6
|
+
* `useVerification()` — overlays a confidence/status icon plus a discrepancy
|
|
7
|
+
* indicator (orthogonal to confidence), click-through to the detail modal.
|
|
8
|
+
* Supports inline contenteditable editing whenever a `saveOverride` transport was
|
|
9
|
+
* passed to `initVerification`; absent it, read-only. With no meta/source it
|
|
10
|
+
* degrades to a bare value — and with no `initVerification` call at all it still
|
|
11
|
+
* renders the bare value and never throws.
|
|
12
|
+
*/
|
|
13
|
+
type __VLS_Props = {
|
|
14
|
+
source?: MetaCarrier;
|
|
15
|
+
field?: string;
|
|
16
|
+
original?: string | number | null;
|
|
17
|
+
format?: (value: unknown) => string;
|
|
18
|
+
};
|
|
19
|
+
declare const _default: __VLS_WithSlots<import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>, {
|
|
20
|
+
default?: ((props: {
|
|
21
|
+
value: unknown;
|
|
22
|
+
formatted: string;
|
|
23
|
+
}) => any) | undefined;
|
|
24
|
+
}>;
|
|
25
|
+
export default _default;
|
|
26
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
27
|
+
new (): {
|
|
28
|
+
$slots: S;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class applied to EVERY piece of library chrome (settings button + panel,
|
|
3
|
+
* overlay icons, discrepancy indicators, comparison-page trigger, modal). A single
|
|
4
|
+
* `@media print` rule hides all of it so the host template prints/exports clean —
|
|
5
|
+
* the library never touches the host's own content. Kept as a constant so
|
|
6
|
+
* components and the print-chrome regression test agree on one source of truth.
|
|
7
|
+
*/
|
|
8
|
+
export declare const CHROME_CLASS = "tv-chrome";
|
|
9
|
+
/** localStorage key holding the persisted overlay enabled/disabled state. */
|
|
10
|
+
export declare const ENABLED_STORAGE_KEY = "template-verification:enabled";
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { type Ref } from "vue";
|
|
2
|
+
import type { SaveOverrideFn } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* The verification module — a composable + module-singleton replacement for the
|
|
5
|
+
* old Vue provide/inject `<VerificationProvider>`.
|
|
6
|
+
*
|
|
7
|
+
* MODULE-SINGLETON INVARIANT
|
|
8
|
+
* --------------------------
|
|
9
|
+
* The backing store is a single module-scoped value, so EVERY `useVerification()`
|
|
10
|
+
* call in the app shares ONE config. This is safe ONLY because each rendered
|
|
11
|
+
* template runs as its own isolated, client-only Vue app inside its own iframe =
|
|
12
|
+
* its own JS module instance. There is exactly one app-wide verification config
|
|
13
|
+
* per module, no SSR, and no second scope — the cases where provide/inject earns
|
|
14
|
+
* its keep (subtree scoping, multiple scopes, SSR-safe singletons) do not apply.
|
|
15
|
+
*
|
|
16
|
+
* If that ever changed — two templates sharing one JS context — this singleton
|
|
17
|
+
* would leak state across them. The fix is to store a per-`app` instance (keyed
|
|
18
|
+
* off the active Vue app instance) behind the SAME `useVerification()` surface;
|
|
19
|
+
* the public API below survives the change, only the storage seam moves.
|
|
20
|
+
*/
|
|
21
|
+
/** Config passed once to {@link initVerification} at App.vue root setup. */
|
|
22
|
+
export interface InitVerificationConfig {
|
|
23
|
+
/** The bound data tree (carries per-node `__meta`). */
|
|
24
|
+
data: Record<string, unknown>;
|
|
25
|
+
/** Host transport; absent ⇒ all fields read-only. */
|
|
26
|
+
saveOverride?: SaveOverrideFn;
|
|
27
|
+
/** Opaque workflow input id echoed back in saves. */
|
|
28
|
+
workflowInputId?: number | string | null;
|
|
29
|
+
/** Seeds the overlay toggle ONLY when localStorage holds no prior value. */
|
|
30
|
+
defaultEnabled?: boolean;
|
|
31
|
+
}
|
|
32
|
+
/** The live verification state returned by {@link useVerification}. */
|
|
33
|
+
export interface Verification {
|
|
34
|
+
/** App-wide overlay toggle, persisted to localStorage. */
|
|
35
|
+
enabled: Ref<boolean>;
|
|
36
|
+
/** The bound data tree (carries per-node `__meta`). */
|
|
37
|
+
data: Record<string, unknown>;
|
|
38
|
+
/** Host transport; absent ⇒ all fields read-only. */
|
|
39
|
+
saveOverride?: SaveOverrideFn;
|
|
40
|
+
/** Opaque workflow input id echoed back in saves. */
|
|
41
|
+
workflowInputId?: number | string | null;
|
|
42
|
+
/** Open the comparison page (rendered by `<VerificationChrome>`). */
|
|
43
|
+
openComparison: () => void;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Establish the app-wide verification config. Call ONCE at App.vue root setup —
|
|
47
|
+
* it is a function call, NOT a wrapping element. Replaces any prior store so the
|
|
48
|
+
* latest config wins (and so tests start clean per init).
|
|
49
|
+
*/
|
|
50
|
+
export declare function initVerification(config: InitVerificationConfig): void;
|
|
51
|
+
/**
|
|
52
|
+
* Consume the live verification state. Returns a default (empty data, disabled,
|
|
53
|
+
* no transport) when {@link initVerification} was never called, so a
|
|
54
|
+
* `<VerifiedField>` used standalone degrades to a bare value instead of throwing.
|
|
55
|
+
* Consumed internally by `VerifiedField` + `VerificationChrome`; exported for
|
|
56
|
+
* advanced hosts.
|
|
57
|
+
*/
|
|
58
|
+
export declare function useVerification(): Verification;
|
|
59
|
+
/**
|
|
60
|
+
* @internal Binds the comparison dialog's open-state for `<VerificationChrome>`.
|
|
61
|
+
* Not part of the public surface — chrome and `openComparison()` share one ref.
|
|
62
|
+
*/
|
|
63
|
+
export declare function useComparisonDialog(): Ref<boolean>;
|
|
64
|
+
/**
|
|
65
|
+
* @internal Test-only: clear the module singleton so each test starts from the
|
|
66
|
+
* uninitialized state. Not exported from the package entry point.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resetVerification(): void;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @thehammer/template-verification — read-write verification overlay for rendered
|
|
3
|
+
* template apps. Consumes the host's `__meta` sidecar (override > extracted >
|
|
4
|
+
* claim resolution + discrepancy), overlays confidence/status icons, a
|
|
5
|
+
* click-through detail modal with inline + modal-driven override editing, and a
|
|
6
|
+
* recursive comparison page. Fetches nothing — the host calls `initVerification`
|
|
7
|
+
* once with `data` and an optional `saveOverride` transport.
|
|
8
|
+
*/
|
|
9
|
+
export { initVerification, useVerification } from "./context";
|
|
10
|
+
export type { InitVerificationConfig, Verification } from "./context";
|
|
11
|
+
export { default as VerifiedField } from "./components/VerifiedField.vue";
|
|
12
|
+
export { default as VerificationChrome } from "./components/VerificationChrome.vue";
|
|
13
|
+
export { default as ConfidenceIcon } from "./components/ConfidenceIcon.vue";
|
|
14
|
+
export { default as VerificationModal } from "./components/VerificationModal.vue";
|
|
15
|
+
export { default as ComparisonPage } from "./components/ComparisonPage.vue";
|
|
16
|
+
export { bucketConfidence, STATUS_COLORS, STATUS_LABELS } from "./lib/confidence";
|
|
17
|
+
export { resolveDataPoint, resolveCandidates, reasoningOf, citedTextOf, RESOLUTION_PRIORITY } from "./lib/resolveDataPoint";
|
|
18
|
+
export { hasDiscrepancy, candidatesDisagree } from "./lib/discrepancy";
|
|
19
|
+
export { walkDataPoints } from "./lib/walk";
|
|
20
|
+
export type { WalkedDataPoint } from "./lib/walk";
|
|
21
|
+
export { groupSources, sourceRowLabel } from "./lib/sources";
|
|
22
|
+
export type { SourceGroup } from "./lib/sources";
|
|
23
|
+
export { CHROME_CLASS, ENABLED_STORAGE_KEY } from "./constants";
|
|
24
|
+
export type { VerificationStatus, ResolvedSource, SourceChoice, VerificationSource, ExtractedCandidate, OverrideCandidate, ClaimCandidate, CandidateSet, DataPointAnchor, ResolvedValue, DataPointMeta, MetaCarrier, SaveOverrideArgs, SaveOverrideFn } from "./types";
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { VerificationStatus } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Bucket the INTEGER confidence (1-5) into a verification status.
|
|
4
|
+
*
|
|
5
|
+
* null / undefined → unverifiable (no extraction provenance)
|
|
6
|
+
* <= 2 → low
|
|
7
|
+
* == 3 → medium
|
|
8
|
+
* >= 4 → high
|
|
9
|
+
*
|
|
10
|
+
* IMPORTANT: this buckets the NUMBER. The host app once shipped a confidence
|
|
11
|
+
* component that string-matched "high"/"medium"/"low"; real data is the integer
|
|
12
|
+
* 1-5, so that component silently fell through to "none" for every value. Do NOT
|
|
13
|
+
* reintroduce string matching here — bucket the integer.
|
|
14
|
+
*/
|
|
15
|
+
export declare function bucketConfidence(confidence?: number | null): VerificationStatus;
|
|
16
|
+
/** Tailwind/text color token per status, for the icon + label. */
|
|
17
|
+
export declare const STATUS_COLORS: Record<VerificationStatus, string>;
|
|
18
|
+
/** Human-readable status label. */
|
|
19
|
+
export declare const STATUS_LABELS: Record<VerificationStatus, string>;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { CandidateSet, DataPointMeta } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* A data point is in discrepancy when two or more PRESENT candidates hold
|
|
4
|
+
* differing values. A single candidate (or none) can never disagree with itself.
|
|
5
|
+
*
|
|
6
|
+
* Discrepancy is ORTHOGONAL to confidence — a high-confidence (>=4) extracted
|
|
7
|
+
* field can still be in discrepancy with a differing claim or override.
|
|
8
|
+
*
|
|
9
|
+
* Prefers the host-computed `meta.discrepancy` flag when present, otherwise
|
|
10
|
+
* derives it from the candidate set per the contract.
|
|
11
|
+
*/
|
|
12
|
+
export declare function hasDiscrepancy(meta?: DataPointMeta | null): boolean;
|
|
13
|
+
/** True when >=2 present candidates hold differing values. */
|
|
14
|
+
export declare function candidatesDisagree(candidates?: CandidateSet | null): boolean;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { CandidateSet, DataPointMeta, ResolvedValue } from "../types";
|
|
2
|
+
/**
|
|
3
|
+
* Candidate resolution priority: a saved override always wins, then the extracted
|
|
4
|
+
* pipeline value, then a claim. Mirrors the host's `RESOLUTION_PRIORITY`. Typed as
|
|
5
|
+
* the candidate keys (excludes the `none` sentinel) so it safely indexes a
|
|
6
|
+
* {@link CandidateSet}.
|
|
7
|
+
*/
|
|
8
|
+
export declare const RESOLUTION_PRIORITY: Array<keyof CandidateSet>;
|
|
9
|
+
/**
|
|
10
|
+
* Resolve a data point to its winning `{ value, source }`.
|
|
11
|
+
*
|
|
12
|
+
* Prefers the host-computed `meta.resolved` when present (the authoritative live
|
|
13
|
+
* shape), otherwise derives it from the candidate set by priority
|
|
14
|
+
* (override > extracted > claim), falling back to `{ value: null, source: 'none' }`
|
|
15
|
+
* when no candidate exists. Either input shape resolves identically.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveDataPoint(meta?: DataPointMeta | null): ResolvedValue;
|
|
18
|
+
/** Derive the winning resolution purely from a candidate set, by priority. */
|
|
19
|
+
export declare function resolveCandidates(candidates?: CandidateSet | null): ResolvedValue;
|
|
20
|
+
/**
|
|
21
|
+
* The extracted candidate's reasoning, normalized to a non-empty string or null.
|
|
22
|
+
* Reasoning is frequently `""`/null — callers render a quiet empty state, never throw.
|
|
23
|
+
*/
|
|
24
|
+
export declare function reasoningOf(meta?: DataPointMeta | null): string | null;
|
|
25
|
+
/**
|
|
26
|
+
* The extracted candidate's cited text, normalized to a non-empty string or null.
|
|
27
|
+
* Like reasoning, cited_text is graceful-empty.
|
|
28
|
+
*/
|
|
29
|
+
export declare function citedTextOf(meta?: DataPointMeta | null): string | null;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { VerificationSource } from "../types";
|
|
2
|
+
/** A set of sources sharing one file identity, for grouped rendering in the modal. */
|
|
3
|
+
export interface SourceGroup {
|
|
4
|
+
/** Display label for the file (file_name, then a derived id, then a fallback). */
|
|
5
|
+
fileLabel: string;
|
|
6
|
+
/** A view/download URL when the host resolved one; the group links only when set. */
|
|
7
|
+
fileUrl?: string;
|
|
8
|
+
/** The sources belonging to this file. */
|
|
9
|
+
sources: VerificationSource[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Group an extracted candidate's sources by file identity so the modal can render
|
|
13
|
+
* one block per file with its pages beneath. Links a group only when a `file_url`
|
|
14
|
+
* is present on any of its sources.
|
|
15
|
+
*/
|
|
16
|
+
export declare function groupSources(sources?: VerificationSource[] | null): SourceGroup[];
|
|
17
|
+
/** Human page/citation label for a single source row. */
|
|
18
|
+
export declare function sourceRowLabel(source: VerificationSource): string;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { DataPointMeta } from "../types";
|
|
2
|
+
/** A flattened data point discovered by walking the `data` tree. */
|
|
3
|
+
export interface WalkedDataPoint {
|
|
4
|
+
/** Dotted display path, e.g. `medical_providers[0].name`. */
|
|
5
|
+
path: string;
|
|
6
|
+
/** Leaf field/label. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** The verification meta when the point is anchored to a source; null when unanchored. */
|
|
9
|
+
meta: DataPointMeta | null;
|
|
10
|
+
/** The raw rendered value (used for unanchored / not-from-source points). */
|
|
11
|
+
value: unknown;
|
|
12
|
+
/** True when the point has no `__meta` entry (a generated/artifact value, not from source). */
|
|
13
|
+
unanchored: boolean;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Depth-first walk of a rendered `data` tree, flattening every data point.
|
|
17
|
+
*
|
|
18
|
+
* For each object node, its `__meta` map yields the ANCHORED data points (one per
|
|
19
|
+
* keyed field). Scalar fields present in `data` but absent from `__meta` (and not
|
|
20
|
+
* the structural `name`/`type` keys) are emitted as UNANCHORED points — values not
|
|
21
|
+
* traceable to a source (generated/artifact output). Recurses into nested objects
|
|
22
|
+
* and arrays. A childless/empty tree yields an empty list without error.
|
|
23
|
+
*/
|
|
24
|
+
export declare function walkDataPoints(data: unknown, basePath?: string): WalkedDataPoint[];
|
package/dist/style.css
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@media print{.tv-chrome{display:none!important}}
|
|
@@ -0,0 +1,648 @@
|
|
|
1
|
+
import { ref as U, watch as ce, defineComponent as M, computed as v, openBlock as l, createElementBlock as s, normalizeClass as L, createElementVNode as n, toDisplayString as f, createCommentVNode as b, createBlock as W, unref as g, withCtx as X, createVNode as A, Fragment as V, renderList as T, createTextVNode as K, withDirectives as Z, vModelText as de, withKeys as ve, withModifiers as fe, renderSlot as pe, isRef as J, vModelCheckbox as me } from "vue";
|
|
2
|
+
import { DanxDialog as ee, DanxIcon as _e, gearIcon as xe } from "@thehammer/danx-ui";
|
|
3
|
+
const D = "tv-chrome", te = "template-verification:enabled";
|
|
4
|
+
let P = null;
|
|
5
|
+
function ye(e) {
|
|
6
|
+
var t;
|
|
7
|
+
try {
|
|
8
|
+
return ((t = globalThis.localStorage) == null ? void 0 : t.getItem(e)) ?? null;
|
|
9
|
+
} catch {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
function ge(e, t) {
|
|
14
|
+
var a;
|
|
15
|
+
try {
|
|
16
|
+
(a = globalThis.localStorage) == null || a.setItem(e, t);
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function he(e) {
|
|
21
|
+
const t = ye(te);
|
|
22
|
+
return t === null ? e : t === "true";
|
|
23
|
+
}
|
|
24
|
+
function ae(e) {
|
|
25
|
+
const t = U(he((e == null ? void 0 : e.defaultEnabled) ?? !1));
|
|
26
|
+
return ce(t, (a) => ge(te, a ? "true" : "false")), {
|
|
27
|
+
enabled: t,
|
|
28
|
+
data: (e == null ? void 0 : e.data) ?? {},
|
|
29
|
+
saveOverride: e == null ? void 0 : e.saveOverride,
|
|
30
|
+
workflowInputId: e == null ? void 0 : e.workflowInputId,
|
|
31
|
+
comparisonOpen: U(!1)
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function ne() {
|
|
35
|
+
return P || (P = ae()), P;
|
|
36
|
+
}
|
|
37
|
+
function At(e) {
|
|
38
|
+
P = ae(e);
|
|
39
|
+
}
|
|
40
|
+
function Y() {
|
|
41
|
+
const e = ne();
|
|
42
|
+
return {
|
|
43
|
+
enabled: e.enabled,
|
|
44
|
+
data: e.data,
|
|
45
|
+
saveOverride: e.saveOverride,
|
|
46
|
+
workflowInputId: e.workflowInputId,
|
|
47
|
+
openComparison: () => {
|
|
48
|
+
e.comparisonOpen.value = !0;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function be() {
|
|
53
|
+
return ne().comparisonOpen;
|
|
54
|
+
}
|
|
55
|
+
function G(e) {
|
|
56
|
+
return typeof e != "number" || Number.isNaN(e) ? "unverifiable" : e <= 2 ? "low" : e === 3 ? "medium" : "high";
|
|
57
|
+
}
|
|
58
|
+
const ke = {
|
|
59
|
+
high: "text-green-600",
|
|
60
|
+
medium: "text-yellow-500",
|
|
61
|
+
low: "text-red-600",
|
|
62
|
+
unverifiable: "text-gray-400"
|
|
63
|
+
}, we = {
|
|
64
|
+
high: "High",
|
|
65
|
+
medium: "Medium",
|
|
66
|
+
low: "Low",
|
|
67
|
+
unverifiable: "Unverifiable"
|
|
68
|
+
}, le = ["override", "extracted", "claim"];
|
|
69
|
+
function H(e) {
|
|
70
|
+
return e ? e.resolved && e.resolved.source && e.resolved.source !== "none" ? e.resolved : Se(e.candidates) : { value: null, source: "none" };
|
|
71
|
+
}
|
|
72
|
+
function Se(e) {
|
|
73
|
+
if (e)
|
|
74
|
+
for (const t of le) {
|
|
75
|
+
const a = e[t];
|
|
76
|
+
if (a != null)
|
|
77
|
+
return { value: a.value, source: t };
|
|
78
|
+
}
|
|
79
|
+
return { value: null, source: "none" };
|
|
80
|
+
}
|
|
81
|
+
function B(e) {
|
|
82
|
+
var c, o;
|
|
83
|
+
const t = (o = (c = e == null ? void 0 : e.candidates) == null ? void 0 : c.extracted) == null ? void 0 : o.reasoning;
|
|
84
|
+
if (t == null) return null;
|
|
85
|
+
const a = String(t).trim();
|
|
86
|
+
return a === "" ? null : a;
|
|
87
|
+
}
|
|
88
|
+
function F(e) {
|
|
89
|
+
var c, o;
|
|
90
|
+
const t = (o = (c = e == null ? void 0 : e.candidates) == null ? void 0 : c.extracted) == null ? void 0 : o.cited_text;
|
|
91
|
+
if (t == null) return null;
|
|
92
|
+
const a = String(t).trim();
|
|
93
|
+
return a === "" ? null : a;
|
|
94
|
+
}
|
|
95
|
+
function se(e) {
|
|
96
|
+
return e ? typeof e.discrepancy == "boolean" ? e.discrepancy : $e(e.candidates) : !1;
|
|
97
|
+
}
|
|
98
|
+
function $e(e) {
|
|
99
|
+
if (!e) return !1;
|
|
100
|
+
const t = le.map((a) => e[a]).filter((a) => a != null).map((a) => Ce(a.value));
|
|
101
|
+
return t.length < 2 ? !1 : new Set(t).size > 1;
|
|
102
|
+
}
|
|
103
|
+
function Ce(e) {
|
|
104
|
+
return JSON.stringify(e ?? null);
|
|
105
|
+
}
|
|
106
|
+
function Ve(e, t, a) {
|
|
107
|
+
var r;
|
|
108
|
+
const c = (r = t.value) == null ? void 0 : r.anchor;
|
|
109
|
+
if (!e.saveOverride || !c) return null;
|
|
110
|
+
const o = e.saveOverride;
|
|
111
|
+
return async (u, p) => {
|
|
112
|
+
const _ = await o({
|
|
113
|
+
workflow_input_id: e.workflowInputId,
|
|
114
|
+
object_ids: c.object_ids,
|
|
115
|
+
field: c.field,
|
|
116
|
+
field_path: c.field_path,
|
|
117
|
+
value: u,
|
|
118
|
+
source_choice: p
|
|
119
|
+
});
|
|
120
|
+
return a(_), _;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
const Oe = ["data-status", "title"], Ie = {
|
|
124
|
+
class: "tv-confidence-glyph",
|
|
125
|
+
"aria-hidden": "true"
|
|
126
|
+
}, Le = {
|
|
127
|
+
key: 0,
|
|
128
|
+
class: "tv-confidence-label text-xs font-medium"
|
|
129
|
+
}, j = /* @__PURE__ */ M({
|
|
130
|
+
__name: "ConfidenceIcon",
|
|
131
|
+
props: {
|
|
132
|
+
confidence: {},
|
|
133
|
+
status: {},
|
|
134
|
+
showLabel: { type: Boolean }
|
|
135
|
+
},
|
|
136
|
+
setup(e) {
|
|
137
|
+
const t = e, a = v(
|
|
138
|
+
() => t.status ?? G(t.confidence)
|
|
139
|
+
), c = {
|
|
140
|
+
high: "✔",
|
|
141
|
+
// check
|
|
142
|
+
medium: "⚠",
|
|
143
|
+
// warning triangle
|
|
144
|
+
low: "✕",
|
|
145
|
+
// x
|
|
146
|
+
unverifiable: "?"
|
|
147
|
+
}, o = v(() => c[a.value]), r = v(() => ke[a.value]), u = v(() => we[a.value]);
|
|
148
|
+
return (p, _) => (l(), s("span", {
|
|
149
|
+
class: L(["tv-confidence-icon inline-flex items-center gap-1", r.value]),
|
|
150
|
+
"data-status": a.value,
|
|
151
|
+
title: u.value
|
|
152
|
+
}, [
|
|
153
|
+
n("span", Ie, f(o.value), 1),
|
|
154
|
+
e.showLabel ? (l(), s("span", Le, f(u.value), 1)) : b("", !0)
|
|
155
|
+
], 10, Oe));
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
function oe(e) {
|
|
159
|
+
if (!e || e.length === 0) return [];
|
|
160
|
+
const t = /* @__PURE__ */ new Map();
|
|
161
|
+
for (const a of e) {
|
|
162
|
+
const c = Ue(a);
|
|
163
|
+
let o = t.get(c);
|
|
164
|
+
o || (o = { fileLabel: Ee(a), fileUrl: a.file_url, sources: [] }, t.set(c, o)), !o.fileUrl && a.file_url && (o.fileUrl = a.file_url), o.sources.push(a);
|
|
165
|
+
}
|
|
166
|
+
return [...t.values()];
|
|
167
|
+
}
|
|
168
|
+
function Ue(e) {
|
|
169
|
+
const t = e.file_name ?? e.file_id ?? e.stored_file_id ?? e.source_id ?? e.id;
|
|
170
|
+
return t != null ? String(t) : "__unknown__";
|
|
171
|
+
}
|
|
172
|
+
function Ee(e) {
|
|
173
|
+
return e.file_name ? e.file_name : e.file_id !== void 0 && e.file_id !== null ? `File ${e.file_id}` : e.stored_file_id !== void 0 && e.stored_file_id !== null ? `File ${e.stored_file_id}` : e.source_type ? e.source_type : "Source";
|
|
174
|
+
}
|
|
175
|
+
function ie(e) {
|
|
176
|
+
return e.page !== void 0 && e.page !== null ? `Page ${e.page}` : e.explanation ? String(e.explanation) : e.source_type ? String(e.source_type) : "Citation";
|
|
177
|
+
}
|
|
178
|
+
const Te = { class: "tv-modal flex flex-col gap-4 text-sm" }, Ae = { class: "tv-modal-resolved" }, De = { class: "flex items-center gap-2" }, Ne = { class: "tv-resolved-value font-semibold" }, Me = { class: "tv-resolved-source text-xs text-gray-500" }, Re = {
|
|
179
|
+
key: 0,
|
|
180
|
+
class: "tv-discrepancy text-xs font-medium text-amber-600"
|
|
181
|
+
}, Pe = { class: "tv-modal-candidates" }, je = { class: "w-full" }, Be = { class: "py-1 pr-2 font-medium" }, Fe = { class: "py-1 pr-2" }, ze = { class: "py-1 text-xs text-gray-500" }, Ke = {
|
|
182
|
+
key: 0,
|
|
183
|
+
class: "tv-modal-provenance flex flex-col gap-1"
|
|
184
|
+
}, Ye = { class: "tv-confidence-value" }, Ge = { class: "tv-reasoning whitespace-pre-line" }, He = { class: "tv-cited-text border-l-2 border-gray-200 pl-2 italic" }, qe = { class: "tv-modal-sources" }, Je = {
|
|
185
|
+
key: 0,
|
|
186
|
+
class: "tv-no-source text-gray-400"
|
|
187
|
+
}, Qe = { class: "font-medium" }, We = ["href"], Xe = {
|
|
188
|
+
key: 1,
|
|
189
|
+
class: "tv-source-name"
|
|
190
|
+
}, Ze = { class: "ml-3 list-disc" }, et = {
|
|
191
|
+
key: 1,
|
|
192
|
+
class: "tv-modal-resolution flex flex-col gap-2 border-t border-gray-200 pt-3"
|
|
193
|
+
}, tt = { class: "flex flex-wrap gap-2" }, at = ["disabled"], nt = ["disabled"], lt = { class: "flex items-center gap-2" }, st = ["disabled"], ot = /* @__PURE__ */ M({
|
|
194
|
+
__name: "VerificationModal",
|
|
195
|
+
props: {
|
|
196
|
+
modelValue: { type: Boolean },
|
|
197
|
+
meta: {},
|
|
198
|
+
label: {},
|
|
199
|
+
save: { type: [Function, null] }
|
|
200
|
+
},
|
|
201
|
+
emits: ["update:modelValue", "saved"],
|
|
202
|
+
setup(e, { emit: t }) {
|
|
203
|
+
const a = e, c = t, o = v({
|
|
204
|
+
get: () => a.modelValue,
|
|
205
|
+
set: (i) => c("update:modelValue", i)
|
|
206
|
+
}), r = v(() => H(a.meta)), u = v(() => a.meta.candidates.extracted), p = v(() => a.meta.candidates.claim), _ = v(() => a.meta.candidates.override), k = v(() => {
|
|
207
|
+
var i;
|
|
208
|
+
return G((i = u.value) == null ? void 0 : i.confidence);
|
|
209
|
+
}), m = v(() => B(a.meta)), d = v(() => F(a.meta)), $ = v(() => {
|
|
210
|
+
var i;
|
|
211
|
+
return oe((i = u.value) == null ? void 0 : i.sources);
|
|
212
|
+
}), O = v(() => {
|
|
213
|
+
const i = [];
|
|
214
|
+
return _.value && i.push({ source: "Override", value: _.value.value, detail: _.value.source_choice ? `via ${_.value.source_choice}` : void 0 }), u.value && i.push({ source: "Extracted", value: u.value.value, detail: E(u.value.confidence) }), p.value && i.push({ source: "Claim", value: p.value.value, detail: p.value.claim_set_label ?? void 0 }), i;
|
|
215
|
+
}), I = U(""), x = U(!1);
|
|
216
|
+
function E(i) {
|
|
217
|
+
return i == null ? "Unverifiable" : `${i}/5`;
|
|
218
|
+
}
|
|
219
|
+
function R(i) {
|
|
220
|
+
return i == null ? "" : String(i);
|
|
221
|
+
}
|
|
222
|
+
async function N(i, y) {
|
|
223
|
+
if (!(!a.save || x.value)) {
|
|
224
|
+
x.value = !0;
|
|
225
|
+
try {
|
|
226
|
+
const w = await a.save(i, y);
|
|
227
|
+
c("saved", w);
|
|
228
|
+
} finally {
|
|
229
|
+
x.value = !1;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const h = () => {
|
|
234
|
+
var i;
|
|
235
|
+
return N(q((i = u.value) == null ? void 0 : i.value), "extracted");
|
|
236
|
+
}, S = () => {
|
|
237
|
+
var i;
|
|
238
|
+
return N(q((i = p.value) == null ? void 0 : i.value), "claim");
|
|
239
|
+
}, C = () => N(I.value, "custom");
|
|
240
|
+
function q(i) {
|
|
241
|
+
return i == null ? null : String(i);
|
|
242
|
+
}
|
|
243
|
+
return (i, y) => (l(), W(g(ee), {
|
|
244
|
+
modelValue: o.value,
|
|
245
|
+
"onUpdate:modelValue": y[1] || (y[1] = (w) => o.value = w),
|
|
246
|
+
title: e.label || "Verification detail",
|
|
247
|
+
"close-x": "",
|
|
248
|
+
class: L(g(D))
|
|
249
|
+
}, {
|
|
250
|
+
default: X(() => [
|
|
251
|
+
n("div", Te, [
|
|
252
|
+
n("section", Ae, [
|
|
253
|
+
y[2] || (y[2] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Resolved value", -1)),
|
|
254
|
+
n("div", De, [
|
|
255
|
+
n("span", Ne, f(R(r.value.value)), 1),
|
|
256
|
+
n("span", Me, "(" + f(r.value.source) + ")", 1),
|
|
257
|
+
A(j, {
|
|
258
|
+
status: k.value,
|
|
259
|
+
"show-label": ""
|
|
260
|
+
}, null, 8, ["status"])
|
|
261
|
+
]),
|
|
262
|
+
e.meta.discrepancy ? (l(), s("div", Re, " ⚠ Sources disagree ")) : b("", !0)
|
|
263
|
+
]),
|
|
264
|
+
n("section", Pe, [
|
|
265
|
+
y[3] || (y[3] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Candidates", -1)),
|
|
266
|
+
n("table", je, [
|
|
267
|
+
n("tbody", null, [
|
|
268
|
+
(l(!0), s(V, null, T(O.value, (w) => (l(), s("tr", {
|
|
269
|
+
key: w.source,
|
|
270
|
+
class: "tv-candidate-row border-t border-gray-100"
|
|
271
|
+
}, [
|
|
272
|
+
n("td", Be, f(w.source), 1),
|
|
273
|
+
n("td", Fe, f(R(w.value)), 1),
|
|
274
|
+
n("td", ze, f(w.detail), 1)
|
|
275
|
+
]))), 128))
|
|
276
|
+
])
|
|
277
|
+
])
|
|
278
|
+
]),
|
|
279
|
+
u.value ? (l(), s("section", Ke, [
|
|
280
|
+
y[6] || (y[6] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Confidence", -1)),
|
|
281
|
+
n("div", Ye, f(E(u.value.confidence)), 1),
|
|
282
|
+
m.value ? (l(), s(V, { key: 0 }, [
|
|
283
|
+
y[4] || (y[4] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Reasoning", -1)),
|
|
284
|
+
n("p", Ge, f(m.value), 1)
|
|
285
|
+
], 64)) : b("", !0),
|
|
286
|
+
d.value ? (l(), s(V, { key: 1 }, [
|
|
287
|
+
y[5] || (y[5] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Cited text", -1)),
|
|
288
|
+
n("blockquote", He, [
|
|
289
|
+
K(f(d.value) + " ", 1),
|
|
290
|
+
n("span", {
|
|
291
|
+
class: L(["tv-cited-verified ml-1 text-xs", u.value.cited_text_verified ? "text-green-600" : "text-gray-400"])
|
|
292
|
+
}, f(u.value.cited_text_verified ? "✔ verified" : "unverified"), 3)
|
|
293
|
+
])
|
|
294
|
+
], 64)) : b("", !0)
|
|
295
|
+
])) : b("", !0),
|
|
296
|
+
n("section", qe, [
|
|
297
|
+
y[7] || (y[7] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Sources", -1)),
|
|
298
|
+
$.value.length === 0 ? (l(), s("div", Je, "No source recorded")) : b("", !0),
|
|
299
|
+
(l(!0), s(V, null, T($.value, (w) => (l(), s("div", {
|
|
300
|
+
key: w.fileLabel,
|
|
301
|
+
class: "tv-source-group"
|
|
302
|
+
}, [
|
|
303
|
+
n("div", Qe, [
|
|
304
|
+
w.fileUrl ? (l(), s("a", {
|
|
305
|
+
key: 0,
|
|
306
|
+
href: w.fileUrl,
|
|
307
|
+
class: "tv-source-link text-blue-600 underline",
|
|
308
|
+
target: "_blank",
|
|
309
|
+
rel: "noopener"
|
|
310
|
+
}, f(w.fileLabel), 9, We)) : (l(), s("span", Xe, f(w.fileLabel), 1))
|
|
311
|
+
]),
|
|
312
|
+
n("ul", Ze, [
|
|
313
|
+
(l(!0), s(V, null, T(w.sources, (re, ue) => (l(), s("li", {
|
|
314
|
+
key: ue,
|
|
315
|
+
class: "tv-source-row text-xs text-gray-600"
|
|
316
|
+
}, f(g(ie)(re)), 1))), 128))
|
|
317
|
+
])
|
|
318
|
+
]))), 128))
|
|
319
|
+
]),
|
|
320
|
+
e.save ? (l(), s("section", et, [
|
|
321
|
+
y[8] || (y[8] = n("div", { class: "text-xs uppercase tracking-wide text-gray-500" }, "Resolve", -1)),
|
|
322
|
+
n("div", tt, [
|
|
323
|
+
u.value ? (l(), s("button", {
|
|
324
|
+
key: 0,
|
|
325
|
+
type: "button",
|
|
326
|
+
class: "tv-accept-extracted rounded border px-2 py-1",
|
|
327
|
+
disabled: x.value,
|
|
328
|
+
onClick: h
|
|
329
|
+
}, "Accept extracted", 8, at)) : b("", !0),
|
|
330
|
+
p.value ? (l(), s("button", {
|
|
331
|
+
key: 1,
|
|
332
|
+
type: "button",
|
|
333
|
+
class: "tv-accept-claim rounded border px-2 py-1",
|
|
334
|
+
disabled: x.value,
|
|
335
|
+
onClick: S
|
|
336
|
+
}, "Accept claim", 8, nt)) : b("", !0)
|
|
337
|
+
]),
|
|
338
|
+
n("div", lt, [
|
|
339
|
+
Z(n("input", {
|
|
340
|
+
"onUpdate:modelValue": y[0] || (y[0] = (w) => I.value = w),
|
|
341
|
+
type: "text",
|
|
342
|
+
class: "tv-custom-input flex-1 rounded border px-2 py-1",
|
|
343
|
+
placeholder: "Type a custom value"
|
|
344
|
+
}, null, 512), [
|
|
345
|
+
[de, I.value]
|
|
346
|
+
]),
|
|
347
|
+
n("button", {
|
|
348
|
+
type: "button",
|
|
349
|
+
class: "tv-accept-custom rounded border px-2 py-1",
|
|
350
|
+
disabled: x.value,
|
|
351
|
+
onClick: C
|
|
352
|
+
}, "Type custom", 8, st)
|
|
353
|
+
])
|
|
354
|
+
])) : b("", !0)
|
|
355
|
+
])
|
|
356
|
+
]),
|
|
357
|
+
_: 1
|
|
358
|
+
}, 8, ["modelValue", "title", "class"]));
|
|
359
|
+
}
|
|
360
|
+
}), it = { class: "tv-field inline-flex items-center gap-1" }, rt = ["contenteditable", "data-editable"], ut = {
|
|
361
|
+
key: 0,
|
|
362
|
+
class: "tv-discrepancy-indicator ml-0.5 text-amber-600",
|
|
363
|
+
title: "Sources disagree",
|
|
364
|
+
"aria-label": "discrepancy"
|
|
365
|
+
}, Dt = /* @__PURE__ */ M({
|
|
366
|
+
__name: "VerifiedField",
|
|
367
|
+
props: {
|
|
368
|
+
source: {},
|
|
369
|
+
field: {},
|
|
370
|
+
original: {},
|
|
371
|
+
format: { type: Function }
|
|
372
|
+
},
|
|
373
|
+
setup(e) {
|
|
374
|
+
const t = e, a = Y(), c = v(() => a.enabled.value), o = U(null), r = v(() => {
|
|
375
|
+
var h;
|
|
376
|
+
return o.value ? o.value : t.source && t.field ? ((h = t.source.__meta) == null ? void 0 : h[t.field]) ?? null : null;
|
|
377
|
+
}), u = v(() => H(r.value)), p = v(() => {
|
|
378
|
+
if (t.original !== void 0 && t.original !== null) return t.original;
|
|
379
|
+
if (r.value) return u.value.value;
|
|
380
|
+
if (t.source && t.field) return t.source[t.field];
|
|
381
|
+
}), _ = v(() => {
|
|
382
|
+
const h = p.value;
|
|
383
|
+
return h == null ? "" : t.format ? t.format(h) : String(h);
|
|
384
|
+
}), k = v(() => {
|
|
385
|
+
var h, S, C;
|
|
386
|
+
return G((C = (S = (h = r.value) == null ? void 0 : h.candidates) == null ? void 0 : S.extracted) == null ? void 0 : C.confidence);
|
|
387
|
+
}), m = v(() => se(r.value)), d = v(() => c.value && !!r.value);
|
|
388
|
+
function $(h) {
|
|
389
|
+
var S;
|
|
390
|
+
o.value = h, (S = t.source) != null && S.__meta && t.field && (t.source.__meta[t.field] = h);
|
|
391
|
+
}
|
|
392
|
+
const O = v(() => Ve(a, r, $)), I = v(() => !!O.value), x = U(!1), E = U(!1);
|
|
393
|
+
function R() {
|
|
394
|
+
r.value && (x.value = !0);
|
|
395
|
+
}
|
|
396
|
+
async function N(h) {
|
|
397
|
+
E.value = !1;
|
|
398
|
+
const C = (h.target.textContent ?? "").trim();
|
|
399
|
+
!O.value || C === _.value || await O.value(C, "custom");
|
|
400
|
+
}
|
|
401
|
+
return (h, S) => (l(), s("span", it, [
|
|
402
|
+
n("span", {
|
|
403
|
+
class: "tv-field-value",
|
|
404
|
+
contenteditable: I.value,
|
|
405
|
+
"data-editable": I.value ? "true" : "false",
|
|
406
|
+
onFocus: S[0] || (S[0] = (C) => E.value = !0),
|
|
407
|
+
onBlur: N,
|
|
408
|
+
onKeydown: S[1] || (S[1] = ve(fe((C) => C.target.blur(), ["prevent"]), ["enter"]))
|
|
409
|
+
}, [
|
|
410
|
+
pe(h.$slots, "default", {
|
|
411
|
+
value: p.value,
|
|
412
|
+
formatted: _.value
|
|
413
|
+
}, () => [
|
|
414
|
+
K(f(_.value), 1)
|
|
415
|
+
])
|
|
416
|
+
], 40, rt),
|
|
417
|
+
d.value ? (l(), s("button", {
|
|
418
|
+
key: 0,
|
|
419
|
+
type: "button",
|
|
420
|
+
class: L(["tv-overlay ml-1 inline-flex items-center", g(D)]),
|
|
421
|
+
title: "View verification detail",
|
|
422
|
+
onClick: R
|
|
423
|
+
}, [
|
|
424
|
+
A(j, { status: k.value }, null, 8, ["status"]),
|
|
425
|
+
m.value ? (l(), s("span", ut, "⚠")) : b("", !0)
|
|
426
|
+
], 2)) : b("", !0),
|
|
427
|
+
r.value ? (l(), W(ot, {
|
|
428
|
+
key: 1,
|
|
429
|
+
modelValue: x.value,
|
|
430
|
+
"onUpdate:modelValue": S[2] || (S[2] = (C) => x.value = C),
|
|
431
|
+
meta: r.value,
|
|
432
|
+
label: e.field,
|
|
433
|
+
save: O.value
|
|
434
|
+
}, null, 8, ["modelValue", "meta", "label", "save"])) : b("", !0)
|
|
435
|
+
]));
|
|
436
|
+
}
|
|
437
|
+
}), ct = /* @__PURE__ */ new Set(["name", "type"]);
|
|
438
|
+
function dt(e, t = "") {
|
|
439
|
+
const a = [];
|
|
440
|
+
return z(e, t, a), a;
|
|
441
|
+
}
|
|
442
|
+
function z(e, t, a) {
|
|
443
|
+
if (Array.isArray(e)) {
|
|
444
|
+
e.forEach((r, u) => z(r, `${t}[${u}]`, a));
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (!Q(e)) return;
|
|
448
|
+
const o = e.__meta ?? {};
|
|
449
|
+
for (const [r, u] of Object.entries(e)) {
|
|
450
|
+
if (r === "__meta") continue;
|
|
451
|
+
const p = t ? `${t}.${r}` : r;
|
|
452
|
+
if (Array.isArray(u) || Q(u)) {
|
|
453
|
+
z(u, p, a);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
const _ = o[r];
|
|
457
|
+
!_ && ct.has(r) || a.push({
|
|
458
|
+
path: p,
|
|
459
|
+
label: r,
|
|
460
|
+
meta: _ ?? null,
|
|
461
|
+
value: u,
|
|
462
|
+
unanchored: !_
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function Q(e) {
|
|
467
|
+
return typeof e == "object" && e !== null && !Array.isArray(e);
|
|
468
|
+
}
|
|
469
|
+
const vt = { class: "tv-comparison-page flex flex-col gap-4" }, ft = {
|
|
470
|
+
key: 0,
|
|
471
|
+
class: "tv-comparison-empty text-gray-400"
|
|
472
|
+
}, pt = {
|
|
473
|
+
key: 1,
|
|
474
|
+
class: "tv-anchored-group flex flex-col gap-3"
|
|
475
|
+
}, mt = { class: "flex items-center gap-2" }, _t = { class: "tv-datapoint-path font-mono text-xs text-gray-500" }, xt = { class: "tv-datapoint-resolved font-medium" }, yt = {
|
|
476
|
+
key: 0,
|
|
477
|
+
class: "tv-datapoint-discrepancy text-xs font-medium text-amber-600"
|
|
478
|
+
}, gt = { class: "tv-datapoint-candidates ml-4 text-xs text-gray-600" }, ht = { class: "font-medium" }, bt = {
|
|
479
|
+
key: 0,
|
|
480
|
+
class: "tv-datapoint-reasoning ml-4 text-xs italic text-gray-500"
|
|
481
|
+
}, kt = {
|
|
482
|
+
key: 1,
|
|
483
|
+
class: "tv-datapoint-cited ml-4 text-xs text-gray-500"
|
|
484
|
+
}, wt = { class: "tv-datapoint-sources ml-4 text-xs text-gray-500" }, St = ["href"], $t = { key: 1 }, Ct = { class: "ml-1" }, Vt = {
|
|
485
|
+
key: 2,
|
|
486
|
+
class: "tv-unanchored-group flex flex-col gap-1"
|
|
487
|
+
}, Ot = { class: "tv-datapoint-path font-mono text-xs text-gray-500" }, It = { class: "tv-datapoint-value" }, Lt = /* @__PURE__ */ M({
|
|
488
|
+
__name: "ComparisonPage",
|
|
489
|
+
setup(e) {
|
|
490
|
+
const t = Y(), a = v(() => dt(t.data)), c = v(() => a.value.filter((k) => !k.unanchored)), o = v(() => a.value.filter((k) => k.unanchored));
|
|
491
|
+
function r(k) {
|
|
492
|
+
const m = H(k).value;
|
|
493
|
+
return m == null ? "" : String(m);
|
|
494
|
+
}
|
|
495
|
+
function u(k) {
|
|
496
|
+
return k == null ? "" : String(k);
|
|
497
|
+
}
|
|
498
|
+
function p(k) {
|
|
499
|
+
var m, d, $;
|
|
500
|
+
return ($ = (d = (m = k.meta) == null ? void 0 : m.candidates) == null ? void 0 : d.extracted) == null ? void 0 : $.confidence;
|
|
501
|
+
}
|
|
502
|
+
function _(k) {
|
|
503
|
+
var $;
|
|
504
|
+
const m = ($ = k.meta) == null ? void 0 : $.candidates;
|
|
505
|
+
if (!m) return [];
|
|
506
|
+
const d = [];
|
|
507
|
+
return m.override && d.push({ source: "override", value: m.override.value }), m.extracted && d.push({ source: "extracted", value: m.extracted.value }), m.claim && d.push({ source: "claim", value: m.claim.value }), d;
|
|
508
|
+
}
|
|
509
|
+
return (k, m) => (l(), s("div", vt, [
|
|
510
|
+
m[1] || (m[1] = n("h2", { class: "tv-comparison-title text-lg font-semibold" }, "Verification comparison", -1)),
|
|
511
|
+
a.value.length === 0 ? (l(), s("p", ft, " No data points to compare. ")) : b("", !0),
|
|
512
|
+
c.value.length ? (l(), s("section", pt, [
|
|
513
|
+
(l(!0), s(V, null, T(c.value, (d) => {
|
|
514
|
+
var $, O, I;
|
|
515
|
+
return l(), s("div", {
|
|
516
|
+
key: d.path,
|
|
517
|
+
class: "tv-datapoint border-t border-gray-100 pt-2"
|
|
518
|
+
}, [
|
|
519
|
+
n("div", mt, [
|
|
520
|
+
n("span", _t, f(d.path), 1),
|
|
521
|
+
n("span", xt, f(r(d.meta)), 1),
|
|
522
|
+
A(j, {
|
|
523
|
+
confidence: p(d),
|
|
524
|
+
"show-label": ""
|
|
525
|
+
}, null, 8, ["confidence"]),
|
|
526
|
+
g(se)(d.meta) ? (l(), s("span", yt, "⚠ discrepancy")) : b("", !0)
|
|
527
|
+
]),
|
|
528
|
+
n("ul", gt, [
|
|
529
|
+
(l(!0), s(V, null, T(_(d), (x) => (l(), s("li", {
|
|
530
|
+
key: x.source,
|
|
531
|
+
class: "tv-candidate"
|
|
532
|
+
}, [
|
|
533
|
+
n("span", ht, f(x.source) + ":", 1),
|
|
534
|
+
K(" " + f(u(x.value)), 1)
|
|
535
|
+
]))), 128))
|
|
536
|
+
]),
|
|
537
|
+
g(B)(d.meta) ? (l(), s("p", bt, f(g(B)(d.meta)), 1)) : b("", !0),
|
|
538
|
+
g(F)(d.meta) ? (l(), s("p", kt, " “" + f(g(F)(d.meta)) + "” ", 1)) : b("", !0),
|
|
539
|
+
n("ul", wt, [
|
|
540
|
+
(l(!0), s(V, null, T(g(oe)((I = (O = ($ = d.meta) == null ? void 0 : $.candidates) == null ? void 0 : O.extracted) == null ? void 0 : I.sources), (x, E) => (l(), s("li", {
|
|
541
|
+
key: E,
|
|
542
|
+
class: "tv-source-group"
|
|
543
|
+
}, [
|
|
544
|
+
x.fileUrl ? (l(), s("a", {
|
|
545
|
+
key: 0,
|
|
546
|
+
href: x.fileUrl,
|
|
547
|
+
class: "tv-source-link text-blue-600 underline",
|
|
548
|
+
target: "_blank",
|
|
549
|
+
rel: "noopener"
|
|
550
|
+
}, f(x.fileLabel), 9, St)) : (l(), s("span", $t, f(x.fileLabel), 1)),
|
|
551
|
+
n("span", Ct, "(" + f(x.sources.map(g(ie)).join(", ")) + ")", 1)
|
|
552
|
+
]))), 128))
|
|
553
|
+
])
|
|
554
|
+
]);
|
|
555
|
+
}), 128))
|
|
556
|
+
])) : b("", !0),
|
|
557
|
+
o.value.length ? (l(), s("section", Vt, [
|
|
558
|
+
m[0] || (m[0] = n("h3", { class: "tv-unanchored-title text-sm font-semibold text-gray-500" }, " Unanchored / not from source ", -1)),
|
|
559
|
+
(l(!0), s(V, null, T(o.value, (d) => (l(), s("div", {
|
|
560
|
+
key: d.path,
|
|
561
|
+
class: "tv-unanchored-datapoint flex items-center gap-2 text-sm"
|
|
562
|
+
}, [
|
|
563
|
+
n("span", Ot, f(d.path), 1),
|
|
564
|
+
n("span", It, f(u(d.value)), 1),
|
|
565
|
+
A(j, { status: "unverifiable" })
|
|
566
|
+
]))), 128))
|
|
567
|
+
])) : b("", !0)
|
|
568
|
+
]));
|
|
569
|
+
}
|
|
570
|
+
}), Ut = { class: "tv-enable-toggle flex items-center justify-between text-sm" }, Nt = /* @__PURE__ */ M({
|
|
571
|
+
__name: "VerificationChrome",
|
|
572
|
+
setup(e) {
|
|
573
|
+
const { enabled: t, openComparison: a } = Y(), c = be(), o = U(!1);
|
|
574
|
+
function r() {
|
|
575
|
+
a(), o.value = !1;
|
|
576
|
+
}
|
|
577
|
+
return (u, p) => (l(), s(V, null, [
|
|
578
|
+
n("button", {
|
|
579
|
+
type: "button",
|
|
580
|
+
class: L(["tv-settings-button fixed bottom-4 right-4 z-50 flex h-10 w-10 items-center justify-center rounded-full bg-gray-800 text-white shadow-lg", g(D)]),
|
|
581
|
+
title: "Verification settings",
|
|
582
|
+
onClick: p[0] || (p[0] = (_) => o.value = !o.value)
|
|
583
|
+
}, [
|
|
584
|
+
A(g(_e), {
|
|
585
|
+
icon: g(xe),
|
|
586
|
+
class: "h-5 w-5"
|
|
587
|
+
}, null, 8, ["icon"])
|
|
588
|
+
], 2),
|
|
589
|
+
o.value ? (l(), s("div", {
|
|
590
|
+
key: 0,
|
|
591
|
+
class: L(["tv-settings-panel fixed bottom-16 right-4 z-50 flex w-56 flex-col gap-3 rounded-lg bg-white p-4 shadow-xl ring-1 ring-gray-200", g(D)])
|
|
592
|
+
}, [
|
|
593
|
+
n("label", Ut, [
|
|
594
|
+
p[3] || (p[3] = n("span", null, "Verification overlay", -1)),
|
|
595
|
+
Z(n("input", {
|
|
596
|
+
"onUpdate:modelValue": p[1] || (p[1] = (_) => J(t) ? t.value = _ : null),
|
|
597
|
+
type: "checkbox",
|
|
598
|
+
class: "tv-enable-checkbox"
|
|
599
|
+
}, null, 512), [
|
|
600
|
+
[me, g(t)]
|
|
601
|
+
])
|
|
602
|
+
]),
|
|
603
|
+
n("button", {
|
|
604
|
+
type: "button",
|
|
605
|
+
class: L(["tv-open-comparison rounded border px-2 py-1 text-sm", g(D)]),
|
|
606
|
+
onClick: r
|
|
607
|
+
}, "Open comparison page", 2)
|
|
608
|
+
], 2)) : b("", !0),
|
|
609
|
+
A(g(ee), {
|
|
610
|
+
modelValue: g(c),
|
|
611
|
+
"onUpdate:modelValue": p[2] || (p[2] = (_) => J(c) ? c.value = _ : null),
|
|
612
|
+
title: "Verification comparison",
|
|
613
|
+
width: 80,
|
|
614
|
+
"close-x": "",
|
|
615
|
+
class: L(g(D))
|
|
616
|
+
}, {
|
|
617
|
+
default: X(() => [
|
|
618
|
+
A(Lt)
|
|
619
|
+
]),
|
|
620
|
+
_: 1
|
|
621
|
+
}, 8, ["modelValue", "class"])
|
|
622
|
+
], 64));
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
export {
|
|
626
|
+
D as CHROME_CLASS,
|
|
627
|
+
Lt as ComparisonPage,
|
|
628
|
+
j as ConfidenceIcon,
|
|
629
|
+
te as ENABLED_STORAGE_KEY,
|
|
630
|
+
le as RESOLUTION_PRIORITY,
|
|
631
|
+
ke as STATUS_COLORS,
|
|
632
|
+
we as STATUS_LABELS,
|
|
633
|
+
Nt as VerificationChrome,
|
|
634
|
+
ot as VerificationModal,
|
|
635
|
+
Dt as VerifiedField,
|
|
636
|
+
G as bucketConfidence,
|
|
637
|
+
$e as candidatesDisagree,
|
|
638
|
+
F as citedTextOf,
|
|
639
|
+
oe as groupSources,
|
|
640
|
+
se as hasDiscrepancy,
|
|
641
|
+
At as initVerification,
|
|
642
|
+
B as reasoningOf,
|
|
643
|
+
Se as resolveCandidates,
|
|
644
|
+
H as resolveDataPoint,
|
|
645
|
+
ie as sourceRowLabel,
|
|
646
|
+
Y as useVerification,
|
|
647
|
+
dt as walkDataPoints
|
|
648
|
+
};
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The verification data contract — the `__meta` sidecar the host emits per data
|
|
3
|
+
* point (see gpt-manager `TemplateDataResource`). The library consumes this shape
|
|
4
|
+
* and NEVER fetches it: the host passes `data` (with embedded `__meta`) and a
|
|
5
|
+
* `saveOverride` transport in via props/provide.
|
|
6
|
+
*
|
|
7
|
+
* A rendered object node carries a `__meta` MAP keyed by leaf field name; each
|
|
8
|
+
* entry is a {@link DataPointMeta} describing that one field's verification state.
|
|
9
|
+
*/
|
|
10
|
+
/** Status bucket derived from the integer 1-5 confidence (orthogonal to discrepancy). */
|
|
11
|
+
export type VerificationStatus = "high" | "medium" | "low" | "unverifiable";
|
|
12
|
+
/** The winning candidate key for a resolved data point, or `none` when no candidate exists. */
|
|
13
|
+
export type ResolvedSource = "override" | "extracted" | "claim" | "none";
|
|
14
|
+
/** The basis a user picked when saving an override. */
|
|
15
|
+
export type SourceChoice = "extracted" | "claim" | "custom";
|
|
16
|
+
/**
|
|
17
|
+
* One citation source for an extracted candidate. The live host shape carries
|
|
18
|
+
* `id`/`source_type`/`stored_file_id`/`explanation`; the optional `file_*`/`page`
|
|
19
|
+
* fields cover the idealized document-page shape. Grouping + linking tolerate
|
|
20
|
+
* either: group by file identity, link only when `file_url` is present.
|
|
21
|
+
*/
|
|
22
|
+
export interface VerificationSource {
|
|
23
|
+
id?: number | string;
|
|
24
|
+
source_type?: string | null;
|
|
25
|
+
source_id?: number | string | null;
|
|
26
|
+
explanation?: string | null;
|
|
27
|
+
stored_file_id?: number | string | null;
|
|
28
|
+
agent_thread_message_id?: number | string | null;
|
|
29
|
+
file_id?: number | string;
|
|
30
|
+
page?: number;
|
|
31
|
+
file_url?: string;
|
|
32
|
+
file_name?: string;
|
|
33
|
+
}
|
|
34
|
+
/** The extracted (pipeline) candidate — carries provenance: confidence, reasoning, citation. */
|
|
35
|
+
export interface ExtractedCandidate {
|
|
36
|
+
value: unknown;
|
|
37
|
+
confidence: number | null;
|
|
38
|
+
reasoning?: string | null;
|
|
39
|
+
cited_text?: string | null;
|
|
40
|
+
cited_text_verified?: boolean | null;
|
|
41
|
+
sources?: VerificationSource[];
|
|
42
|
+
}
|
|
43
|
+
/** A user-saved override candidate — always the highest-priority resolution. */
|
|
44
|
+
export interface OverrideCandidate {
|
|
45
|
+
value: unknown;
|
|
46
|
+
source_choice?: SourceChoice | string;
|
|
47
|
+
set_by?: number | string | null;
|
|
48
|
+
set_at?: string | null;
|
|
49
|
+
}
|
|
50
|
+
/** A claim candidate asserted by an example/reference output — lowest priority. */
|
|
51
|
+
export interface ClaimCandidate {
|
|
52
|
+
value: unknown;
|
|
53
|
+
claim_set_id?: number | string | null;
|
|
54
|
+
claim_set_label?: string | null;
|
|
55
|
+
}
|
|
56
|
+
/** The candidate set present for a data point, keyed by source. */
|
|
57
|
+
export interface CandidateSet {
|
|
58
|
+
override?: OverrideCandidate;
|
|
59
|
+
extracted?: ExtractedCandidate;
|
|
60
|
+
claim?: ClaimCandidate;
|
|
61
|
+
}
|
|
62
|
+
/** The anchor identifying a data point within the host's TeamObject tree. */
|
|
63
|
+
export interface DataPointAnchor {
|
|
64
|
+
object_ids: Array<number | string>;
|
|
65
|
+
field: string;
|
|
66
|
+
field_path: string;
|
|
67
|
+
}
|
|
68
|
+
/** The resolved value + winning source for a data point. */
|
|
69
|
+
export interface ResolvedValue {
|
|
70
|
+
value: unknown;
|
|
71
|
+
source: ResolvedSource;
|
|
72
|
+
}
|
|
73
|
+
/** The per-data-point verification metadata (one `__meta` map entry). */
|
|
74
|
+
export interface DataPointMeta {
|
|
75
|
+
anchor: DataPointAnchor;
|
|
76
|
+
candidates: CandidateSet;
|
|
77
|
+
discrepancy: boolean;
|
|
78
|
+
resolved: ResolvedValue;
|
|
79
|
+
}
|
|
80
|
+
/** An object node in the rendered `data` tree carrying a keyed `__meta` map. */
|
|
81
|
+
export type MetaCarrier = Record<string, unknown> & {
|
|
82
|
+
__meta?: Record<string, DataPointMeta>;
|
|
83
|
+
};
|
|
84
|
+
/** Arguments the library passes to the host-injected save transport (guide §7.4). */
|
|
85
|
+
export interface SaveOverrideArgs {
|
|
86
|
+
workflow_input_id: number | string | null | undefined;
|
|
87
|
+
object_ids: Array<number | string>;
|
|
88
|
+
field: string;
|
|
89
|
+
field_path: string;
|
|
90
|
+
value: string | null;
|
|
91
|
+
source_choice: SourceChoice;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The host-injected save transport. Backend-agnostic: the host supplies the
|
|
95
|
+
* authenticated request and returns the recomputed {@link DataPointMeta}. Its mere
|
|
96
|
+
* presence means edits are authorized; absence makes every field read-only.
|
|
97
|
+
*/
|
|
98
|
+
export type SaveOverrideFn = (args: SaveOverrideArgs) => Promise<DataPointMeta> | DataPointMeta;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Ref } from "vue";
|
|
2
|
+
import type { Verification } from "./context";
|
|
3
|
+
import type { DataPointMeta, SourceChoice } from "./types";
|
|
4
|
+
/** A bound save: commit an override (pinned candidate or custom value). */
|
|
5
|
+
export type SaveDataPointFn = (value: string | null, sourceChoice: SourceChoice) => Promise<DataPointMeta>;
|
|
6
|
+
/** The transport-bearing slice of the verification state a save needs. */
|
|
7
|
+
export type SaveContext = Pick<Verification, "saveOverride" | "workflowInputId">;
|
|
8
|
+
/**
|
|
9
|
+
* Build a save function for one data point, bound to its anchor + the host
|
|
10
|
+
* transport. Reads the anchor from the CURRENT meta, commits an override, and
|
|
11
|
+
* hands the host's recomputed {@link DataPointMeta} to `onSaved` so the caller can
|
|
12
|
+
* update its writable state and re-render the new resolution.
|
|
13
|
+
*
|
|
14
|
+
* Returns null when no transport was passed to `initVerification` OR the meta
|
|
15
|
+
* carries no anchor — both mean the field is read-only.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useSaveDataPoint(verification: SaveContext, currentMeta: Ref<DataPointMeta | null | undefined>, onSaved: (meta: DataPointMeta) => void): SaveDataPointFn | null;
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thehammer/template-verification",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Read-write verification overlay for rendered template apps — per-data-point confidence, discrepancy, 3-source resolution (override > extracted > claim), inline edit, and a recursive comparison page. Consumes the host's __meta sidecar; fetches nothing.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/newms87/gpt-manager.git",
|
|
10
|
+
"directory": "template-verification"
|
|
11
|
+
},
|
|
12
|
+
"main": "dist/template-verification.js",
|
|
13
|
+
"module": "dist/template-verification.js",
|
|
14
|
+
"types": "dist/index.d.ts",
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./dist/index.d.ts",
|
|
18
|
+
"import": "./dist/template-verification.js"
|
|
19
|
+
},
|
|
20
|
+
"./style.css": "./dist/style.css"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "vite build && vue-tsc --declaration --emitDeclarationOnly -p tsconfig.build.json",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"type-check": "vue-tsc --noEmit -p tsconfig.json"
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@thehammer/danx-ui": ">=0.8.0",
|
|
37
|
+
"vue": "^3.4.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@thehammer/danx-ui": "^0.8.16",
|
|
41
|
+
"@vitejs/plugin-vue": "^5.0.0",
|
|
42
|
+
"@vue/test-utils": "^2.4.6",
|
|
43
|
+
"happy-dom": "^20.3.9",
|
|
44
|
+
"typescript": "~5.4.0",
|
|
45
|
+
"vite": "^5.4.0",
|
|
46
|
+
"vitest": "^2.1.0",
|
|
47
|
+
"vue": "^3.4.21",
|
|
48
|
+
"vue-tsc": "^2.0.11"
|
|
49
|
+
}
|
|
50
|
+
}
|