@happyvertical/smrt-ingestion 0.57.20 → 0.57.22
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/FEEDBACK.md +137 -0
- package/PROPOSALS.md +146 -5
- package/README.md +168 -2
- package/REVIEW.md +611 -7
- package/agents/execution.md +87 -0
- package/dist/chunks/{execution-NoDh3pjs.js → execution-BzVQax0n.js} +528 -42
- package/dist/chunks/execution-BzVQax0n.js.map +1 -0
- package/dist/chunks/{feedback-CJB900iG.js → feedback-D3UTF8fm.js} +175 -36
- package/dist/chunks/feedback-D3UTF8fm.js.map +1 -0
- package/dist/chunks/{proposal-errors-VxcTU6Db.js → proposal-contracts-77Uo1B1j.js} +38 -44
- package/dist/chunks/proposal-contracts-77Uo1B1j.js.map +1 -0
- package/dist/chunks/reviewer-instruction-CEc3AOuU.js +33 -0
- package/dist/chunks/reviewer-instruction-CEc3AOuU.js.map +1 -0
- package/dist/dto.d.ts +75 -0
- package/dist/dto.d.ts.map +1 -1
- package/dist/execution-contracts.d.ts +4 -1
- package/dist/execution-contracts.d.ts.map +1 -1
- package/dist/execution-dto.d.ts +16 -0
- package/dist/execution-dto.d.ts.map +1 -1
- package/dist/execution-internal.d.ts +2 -0
- package/dist/execution-internal.d.ts.map +1 -1
- package/dist/execution.d.ts +101 -4
- package/dist/execution.d.ts.map +1 -1
- package/dist/execution.js +2 -2
- package/dist/feedback-dto.d.ts +18 -4
- package/dist/feedback-dto.d.ts.map +1 -1
- package/dist/feedback-internal.d.ts +9 -0
- package/dist/feedback-internal.d.ts.map +1 -1
- package/dist/feedback.d.ts +10 -0
- package/dist/feedback.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/manifest.json +339 -3
- package/dist/models.d.ts +15 -0
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +82 -2
- package/dist/models.js.map +1 -1
- package/dist/policy.d.ts +3 -0
- package/dist/policy.d.ts.map +1 -1
- package/dist/presented-filename.d.ts +11 -0
- package/dist/presented-filename.d.ts.map +1 -0
- package/dist/proposal-contracts.d.ts +31 -6
- package/dist/proposal-contracts.d.ts.map +1 -1
- package/dist/proposal-dto.d.ts +17 -0
- package/dist/proposal-dto.d.ts.map +1 -1
- package/dist/proposal-errors.d.ts +20 -0
- package/dist/proposal-errors.d.ts.map +1 -1
- package/dist/proposal-validation.d.ts +26 -0
- package/dist/proposal-validation.d.ts.map +1 -1
- package/dist/proposals.d.ts +6 -1
- package/dist/proposals.d.ts.map +1 -1
- package/dist/proposals.js +268 -71
- package/dist/proposals.js.map +1 -1
- package/dist/redaction.d.ts +9 -0
- package/dist/redaction.d.ts.map +1 -0
- package/dist/review-dto.d.ts +108 -5
- package/dist/review-dto.d.ts.map +1 -1
- package/dist/review-host.d.ts +52 -3
- package/dist/review-host.d.ts.map +1 -1
- package/dist/review-instruction-view.d.ts +12 -0
- package/dist/review-instruction-view.d.ts.map +1 -0
- package/dist/review-summary.d.ts.map +1 -1
- package/dist/reviewer-instruction.d.ts +20 -0
- package/dist/reviewer-instruction.d.ts.map +1 -0
- package/dist/server.d.ts +252 -13
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +1190 -85
- package/dist/server.js.map +1 -1
- package/dist/smrt-knowledge.json +137 -23
- package/dist/sources/common.d.ts +11 -0
- package/dist/sources/common.d.ts.map +1 -1
- package/dist/sources/email.d.ts.map +1 -1
- package/dist/sources/http.d.ts.map +1 -1
- package/dist/sources/watch-folder.d.ts.map +1 -1
- package/dist/svelte/components/ActionReview.svelte +54 -55
- package/dist/svelte/components/ActionReview.svelte.d.ts +8 -1
- package/dist/svelte/components/ActionReview.svelte.d.ts.map +1 -1
- package/dist/svelte/components/CandidatePicker.svelte +72 -0
- package/dist/svelte/components/CandidatePicker.svelte.d.ts +15 -0
- package/dist/svelte/components/CandidatePicker.svelte.d.ts.map +1 -0
- package/dist/svelte/components/EvidenceViewer.svelte +60 -4
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts +12 -1
- package/dist/svelte/components/EvidenceViewer.svelte.d.ts.map +1 -1
- package/dist/svelte/components/InboxView.svelte +172 -19
- package/dist/svelte/components/InboxView.svelte.d.ts +36 -2
- package/dist/svelte/components/InboxView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeInbox.svelte +6 -3
- package/dist/svelte/components/IntakeInbox.svelte.d.ts +4 -1
- package/dist/svelte/components/IntakeInbox.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeReview.svelte +4 -2
- package/dist/svelte/components/IntakeReview.svelte.d.ts +2 -0
- package/dist/svelte/components/IntakeReview.svelte.d.ts.map +1 -1
- package/dist/svelte/components/IntakeWorkspace.svelte +27 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts +19 -0
- package/dist/svelte/components/IntakeWorkspace.svelte.d.ts.map +1 -0
- package/dist/svelte/components/ReviewView.svelte +653 -71
- package/dist/svelte/components/ReviewView.svelte.d.ts +20 -1
- package/dist/svelte/components/ReviewView.svelte.d.ts.map +1 -1
- package/dist/svelte/components/TriageView.svelte +669 -0
- package/dist/svelte/components/TriageView.svelte.d.ts +16 -0
- package/dist/svelte/components/TriageView.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +56 -0
- package/dist/svelte/i18n.d.ts.map +1 -1
- package/dist/svelte/i18n.js +56 -0
- package/dist/svelte/index.d.ts +2 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +1 -0
- package/dist/svelte/view-utils.d.ts +67 -0
- package/dist/svelte/view-utils.d.ts.map +1 -1
- package/dist/svelte/view-utils.js +157 -0
- package/package.json +13 -13
- package/dist/chunks/execution-NoDh3pjs.js.map +0 -1
- package/dist/chunks/execution-internal-DWteDHM2.js +0 -7
- package/dist/chunks/execution-internal-DWteDHM2.js.map +0 -1
- package/dist/chunks/feedback-CJB900iG.js.map +0 -1
- package/dist/chunks/proposal-errors-VxcTU6Db.js.map +0 -1
package/dist/sources/common.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReceiptResult } from '../dto.js';
|
|
2
|
+
import { presentedFilename } from '../presented-filename.js';
|
|
2
3
|
import type { IngestionService, IntakePart, ReceiveInput } from '../server.js';
|
|
3
4
|
/** Host configuration, reconstructed from authenticated session/account state. */
|
|
4
5
|
export interface SourceBinding {
|
|
@@ -22,6 +23,16 @@ export declare class SourceInputError extends Error {
|
|
|
22
23
|
export declare function record(value: unknown): Record<string, unknown>;
|
|
23
24
|
export declare function keys(value: Record<string, unknown>, allowed: string[]): void;
|
|
24
25
|
export declare function text(value: unknown): string;
|
|
26
|
+
/**
|
|
27
|
+
* A name a source presented for a part (upload, attachment, folder entry),
|
|
28
|
+
* kept for labels only (#4214); see `../presented-filename.ts` for the unsafe
|
|
29
|
+
* character set it removes.
|
|
30
|
+
*/
|
|
31
|
+
export { presentedFilename };
|
|
32
|
+
/** `{ filename }` for a usable presented name, else nothing to spread. */
|
|
33
|
+
export declare function withPresentedFilename(value: unknown): {
|
|
34
|
+
filename?: string;
|
|
35
|
+
};
|
|
25
36
|
export declare function timestamp(value: unknown): Date;
|
|
26
37
|
/** Bound bytes before parsing or handing them to the receipt service. */
|
|
27
38
|
export declare function boundedBytes(stream: ReadableStream<Uint8Array> | null, limit: number): Promise<Uint8Array>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/sources/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE/E,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjD,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC/B,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AACD,MAAM,MAAM,YAAY,GACpB,aAAa,GACb;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;CACvE,CAAC;AACN,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,QAAQ,EACpB,SAAS,GACT,OAAO,GACP,kBAAkB,GAClB,mBAAmB;gBAJP,QAAQ,EACpB,SAAS,GACT,OAAO,GACP,kBAAkB,GAClB,mBAAmB;CAI1B;AACD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI9D;AACD,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAG5E;AACD,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI3C;AACD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAI9C;AACD,yEAAyE;AACzE,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,EACzC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CAsBrB;AACD,sFAAsF;AACtF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,IAAI,CAmCN;AACD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EACjB,KAAK,EAAE,UAAU,EAAE,GAClB,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"common.d.ts","sourceRoot":"","sources":["../../src/sources/common.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE/E,kFAAkF;AAClF,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,SAAS,CAAC,CAAC;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,YAAY,CAAC,iBAAiB,CAAC,CAAC;IACjD,SAAS,EAAE,YAAY,CAAC,WAAW,CAAC,CAAC;IACrC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC/B,iBAAiB,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AACD,MAAM,MAAM,YAAY,GACpB,aAAa,GACb;IACE,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,gBAAgB,GAAG,kBAAkB,GAAG,mBAAmB,CAAC;CACvE,CAAC;AACN,qBAAa,gBAAiB,SAAQ,KAAK;aAEvB,QAAQ,EACpB,SAAS,GACT,OAAO,GACP,kBAAkB,GAClB,mBAAmB;gBAJP,QAAQ,EACpB,SAAS,GACT,OAAO,GACP,kBAAkB,GAClB,mBAAmB;CAI1B;AACD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAI9D;AACD,wBAAgB,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,IAAI,CAG5E;AACD,wBAAgB,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAI3C;AACD;;;;GAIG;AACH,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAC7B,0EAA0E;AAC1E,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAGA;AACD,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAI9C;AACD,yEAAyE;AACzE,wBAAsB,YAAY,CAChC,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,GAAG,IAAI,EACzC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,UAAU,CAAC,CAsBrB;AACD,sFAAsF;AACtF,wBAAgB,aAAa,CAC3B,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,SAAS,MAAM,EAAE,GACzB,IAAI,CAmCN;AACD,wBAAsB,OAAO,CAC3B,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,IAAI,EACjB,KAAK,EAAE,UAAU,EAAE,GAClB,OAAO,CAAC,YAAY,CAAC,CAiCvB;AACD,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,YAAY,CAIxD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/sources/email.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,YAAY,EAGlB,MAAM,aAAa,CAAC;AAErB,+FAA+F;AAC/F,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,KAAK,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC,CAAC;CACJ;AACD,MAAM,WAAW,aAAa;IAC5B,sGAAsG;IACtG,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnE,oFAAoF;IACpF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AACD,iGAAiG;AACjG,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,kBAAkB;IAClD,OAAO,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"email.d.ts","sourceRoot":"","sources":["../../src/sources/email.ts"],"names":[],"mappings":"AACA,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,YAAY,EAGlB,MAAM,aAAa,CAAC;AAErB,+FAA+F;AAC/F,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,KAAK,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,UAAU,CAAC;KACnB,CAAC,CAAC;CACJ;AACD,MAAM,WAAW,aAAa;IAC5B,sGAAsG;IACtG,OAAO,EAAE,MAAM,CAAC;IAChB,2FAA2F;IAC3F,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AACD,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,aAAa,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACnE,oFAAoF;IACpF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AACD,iGAAiG;AACjG,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,kBAAkB;IAClD,OAAO,CAAC,QAAQ,EAAE,aAAa,GAAG,OAAO,CAAC,YAAY,CAAC;IAgGvD,YAAY,CAChB,UAAU,EAAE,SAAS,aAAa,EAAE,GACnC,OAAO,CAAC,YAAY,EAAE,CAAC;CAS3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/sources/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/sources/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAML,KAAK,aAAa,EAMnB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,2EAA2E;IAC3E,eAAe,EAAE,MAAM,CAAC;IACxB,6EAA6E;IAC7E,iBAAiB,CAAC,CAAC,SAAS,EAAE;QAC5B,KAAK,EAAE,OAAO,GAAG,SAAS,CAAC;QAC3B,EAAE,EAAE,MAAM,CAAC;QACX,OAAO,EAAE,MAAM,CAAC;KACjB,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;CAC3B;AACD,MAAM,WAAW,iBAAiB;IAChC,2FAA2F;IAC3F,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAAC;CACrE;AAeD,8FAA8F;AAC9F,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,iBAAiB,GACzB,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoJzC;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,aAAa,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,IAAI,CAAC;IAClB,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AACD,uFAAuF;AACvF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE;IAClD,eAAe,EAAE,MAAM,CAAC;IACxB,MAAM,CACJ,OAAO,EAAE,OAAO,EAChB,aAAa,EAAE,UAAU,GACxB,OAAO,CAAC,sBAAsB,GAAG,IAAI,CAAC,CAAC;CAC3C,GAAG,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAoB1C"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"watch-folder.d.ts","sourceRoot":"","sources":["../../src/sources/watch-folder.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,aAAa,CAAC;IACvB,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,CAAC,CAAC,QAAQ,EAAE,SAAS,GAAG,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AACD,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;CACtB;AAYD,gGAAgG;AAChG,qBAAa,wBAAwB;IAGvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,OAAO,CAAS;gBACK,OAAO,EAAE,kBAAkB;YAI1C,MAAM;YAmBN,aAAa;YAQb,WAAW;
|
|
1
|
+
{"version":3,"file":"watch-folder.d.ts","sourceRoot":"","sources":["../../src/sources/watch-folder.ts"],"names":[],"mappings":"AAYA,OAAO,EAEL,KAAK,aAAa,EAElB,KAAK,YAAY,EAElB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,aAAa,CAAC;IACvB,oFAAoF;IACpF,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,iFAAiF;IACjF,UAAU,CAAC,CAAC,QAAQ,EAAE,SAAS,GAAG,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E;AACD,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;CACtB;AAYD,gGAAgG;AAChG,qBAAa,wBAAwB;IAGvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAFpC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAkC;IAC/D,OAAO,CAAC,OAAO,CAAS;gBACK,OAAO,EAAE,kBAAkB;YAI1C,MAAM;YAmBN,aAAa;YAQb,WAAW;IAwFnB,IAAI,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;CA2F3C"}
|
|
@@ -1,35 +1,50 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
/** A reviewer operation whose result may finish the item for this reviewer. */
|
|
3
|
+
export type ReviewOutcome = 'approve' | 'apply' | 'resolve';
|
|
4
|
+
</script>
|
|
1
5
|
<script lang="ts">
|
|
2
|
-
import { Input,
|
|
6
|
+
import { Input, Textarea } from '@happyvertical/smrt-ui/forms';
|
|
3
7
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
4
8
|
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
5
9
|
import { onDestroy, untrack } from 'svelte';
|
|
6
10
|
import type { ReviewInput } from '../../execution-dto.js';
|
|
7
|
-
import type {
|
|
8
|
-
CandidatePage,
|
|
9
|
-
ProposalCatalogEntry,
|
|
10
|
-
} from '../../proposal-dto.js';
|
|
11
|
+
import type { ProposalCatalogEntry } from '../../proposal-dto.js';
|
|
11
12
|
import type { IntakeReviewHost, ReviewAction } from '../../review-dto.js';
|
|
12
13
|
import { M } from '../i18n.js';
|
|
13
|
-
import { argumentsObject } from '../view-utils.js';
|
|
14
|
+
import { argumentsObject, missingRequired } from '../view-utils.js';
|
|
15
|
+
import CandidatePicker from './CandidatePicker.svelte';
|
|
14
16
|
export interface Props {
|
|
15
17
|
host: IntakeReviewHost;
|
|
16
18
|
itemId: string;
|
|
17
19
|
action: ReviewAction;
|
|
18
20
|
catalog?: ProposalCatalogEntry;
|
|
19
21
|
currentAttemptId?: string;
|
|
20
|
-
|
|
22
|
+
/** A resolved item is terminal: approve/correct/defer/apply and edits are hidden; only reject remains. */
|
|
23
|
+
resolved?: boolean;
|
|
24
|
+
/** `outcome` names an approve or apply so the parent can report it after its reload. */
|
|
25
|
+
run: (
|
|
26
|
+
operation: () => Promise<unknown>,
|
|
27
|
+
outcome?: ReviewOutcome,
|
|
28
|
+
) => Promise<void>;
|
|
21
29
|
deny: () => void;
|
|
30
|
+
/** Whether this editor holds unsent input (arguments, reason, comment). */
|
|
31
|
+
ondirty?: (dirty: boolean) => void;
|
|
22
32
|
}
|
|
23
|
-
let {
|
|
24
|
-
|
|
33
|
+
let {
|
|
34
|
+
host,
|
|
35
|
+
itemId,
|
|
36
|
+
action,
|
|
37
|
+
catalog,
|
|
38
|
+
currentAttemptId,
|
|
39
|
+
resolved = false,
|
|
40
|
+
run,
|
|
41
|
+
deny,
|
|
42
|
+
ondirty,
|
|
43
|
+
}: Props = $props();
|
|
25
44
|
const { t } = useI18n();
|
|
26
45
|
// ReviewView keys this editor by action/revision/reviewVersion; edits are local to that binding.
|
|
27
46
|
let args = $state(untrack(() => JSON.stringify(action.args, null, 2) ?? '')),
|
|
28
47
|
reason = $state(''),
|
|
29
|
-
query = $state(''),
|
|
30
|
-
field = $state(''),
|
|
31
|
-
candidates = $state<CandidatePage>(),
|
|
32
|
-
searching = $state(false),
|
|
33
48
|
invalid = $state(false),
|
|
34
49
|
comment = $state(''),
|
|
35
50
|
planArgs = $state(
|
|
@@ -38,11 +53,18 @@ let args = $state(untrack(() => JSON.stringify(action.args, null, 2) ?? '')),
|
|
|
38
53
|
let alive = true;
|
|
39
54
|
onDestroy(() => {
|
|
40
55
|
alive = false;
|
|
56
|
+
// The editor's local input goes with it.
|
|
57
|
+
ondirty?.(false);
|
|
41
58
|
});
|
|
42
59
|
const dirty = $derived(
|
|
43
60
|
args !== (JSON.stringify(action.args, null, 2) ?? '') ||
|
|
44
61
|
planArgs !== (JSON.stringify(action.plan?.args, null, 2) ?? ''),
|
|
45
62
|
);
|
|
63
|
+
const unsent = $derived(dirty || !!reason || !!comment);
|
|
64
|
+
$effect(() => {
|
|
65
|
+
const edited = unsent;
|
|
66
|
+
untrack(() => ondirty?.(edited));
|
|
67
|
+
});
|
|
46
68
|
const waiting = $derived(
|
|
47
69
|
action.args !== undefined && action.review.state === 'waiting_review',
|
|
48
70
|
);
|
|
@@ -64,6 +86,13 @@ const editable = $derived(
|
|
|
64
86
|
const planEditable = $derived(
|
|
65
87
|
action.args !== undefined && (waiting || revisable),
|
|
66
88
|
);
|
|
89
|
+
// Required handler arguments absent from the current draft; a hint only,
|
|
90
|
+
// the server-side preview still validates the published arguments (#4208).
|
|
91
|
+
const missing = $derived(
|
|
92
|
+
catalog && editable && !action.plan
|
|
93
|
+
? missingRequired(catalog.argsSchema, args)
|
|
94
|
+
: [],
|
|
95
|
+
);
|
|
67
96
|
async function decide(decision: ReviewInput['decision']) {
|
|
68
97
|
if (!waiting) return;
|
|
69
98
|
let correctedArgs: ReviewInput['correctedArgs'];
|
|
@@ -86,6 +115,7 @@ async function decide(decision: ReviewInput['decision']) {
|
|
|
86
115
|
...(reason ? { reason } : {}),
|
|
87
116
|
...(correctedArgs ? { correctedArgs } : {}),
|
|
88
117
|
}),
|
|
118
|
+
decision === 'approve' ? 'approve' : undefined,
|
|
89
119
|
);
|
|
90
120
|
}
|
|
91
121
|
async function saveEdits() {
|
|
@@ -120,24 +150,6 @@ async function saveEdits() {
|
|
|
120
150
|
};
|
|
121
151
|
await run(() => host.editAction?.(input) ?? Promise.resolve());
|
|
122
152
|
}
|
|
123
|
-
async function search() {
|
|
124
|
-
if (!action.handlerId || !action.handlerVersion || searching) return;
|
|
125
|
-
searching = true;
|
|
126
|
-
candidates = undefined;
|
|
127
|
-
try {
|
|
128
|
-
const result = await host.candidates({
|
|
129
|
-
itemId,
|
|
130
|
-
handlerId: action.handlerId,
|
|
131
|
-
handlerVersion: action.handlerVersion,
|
|
132
|
-
query,
|
|
133
|
-
});
|
|
134
|
-
if (alive) candidates = result;
|
|
135
|
-
} catch {
|
|
136
|
-
if (alive) deny();
|
|
137
|
-
} finally {
|
|
138
|
-
if (alive) searching = false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
153
|
async function editPlan() {
|
|
142
154
|
const plan = action.plan ?? action.stalePlan;
|
|
143
155
|
const attemptId = action.plan?.attemptId ?? currentAttemptId;
|
|
@@ -168,18 +180,9 @@ async function editPlan() {
|
|
|
168
180
|
};
|
|
169
181
|
await run(() => host.editPlan?.(input) ?? Promise.resolve());
|
|
170
182
|
}
|
|
171
|
-
function choose(id: string) {
|
|
183
|
+
function choose(field: string, id: string) {
|
|
172
184
|
try {
|
|
173
|
-
|
|
174
|
-
const ref = catalog?.references[field];
|
|
175
|
-
if (
|
|
176
|
-
ref?.kind !== 'candidate' ||
|
|
177
|
-
!candidates?.items.some(
|
|
178
|
-
(candidate) => candidate.id === id && candidate.model === ref.model,
|
|
179
|
-
)
|
|
180
|
-
)
|
|
181
|
-
return;
|
|
182
|
-
args = JSON.stringify({ ...values, [field]: id }, null, 2);
|
|
185
|
+
args = JSON.stringify({ ...argumentsObject(args), [field]: id }, null, 2);
|
|
183
186
|
} catch {
|
|
184
187
|
invalid = true;
|
|
185
188
|
}
|
|
@@ -187,45 +190,41 @@ function choose(id: string) {
|
|
|
187
190
|
</script>
|
|
188
191
|
<article data-testid={`action-${action.review.actionId}`}>
|
|
189
192
|
<h3>{action.handlerId ?? action.review.actionId}</h3>
|
|
193
|
+
{#if action.origin === 'reviewer-selected'}<p>{t(M['ingestion.reviewerSelected'])}</p>{/if}
|
|
190
194
|
<p>{t(M['ingestion.state'])}: {action.review.state}</p>
|
|
191
195
|
{#if action.args !== undefined}
|
|
192
196
|
<h4>{t(M['ingestion.effects'])}</h4><pre>{JSON.stringify(action.review.display,null,2)}</pre>
|
|
193
197
|
<Textarea aria-label={t(M['ingestion.args'])} bind:value={args} readonly={!editable || !!action.plan} rows={8} />
|
|
194
198
|
{#if editable}
|
|
199
|
+
{#if missing.length}<h4>{t(M['ingestion.missing'])}</h4><ul>{#each missing as name (name)}<li>{name}</li>{/each}</ul>{/if}
|
|
195
200
|
<Input aria-label={t(M['ingestion.reason'])} bind:value={reason} />
|
|
196
|
-
{#if !action.plan && catalog
|
|
197
|
-
<h4>{t(M['ingestion.candidates'])}</h4>
|
|
198
|
-
<Select aria-label={t(M['ingestion.field'])} bind:value={field}><option value="">{t(M['ingestion.field'])}</option>{#each Object.entries(catalog.references) as [name,ref]}{#if ref.kind === 'candidate'}<option value={name}>{name}</option>{/if}{/each}</Select>
|
|
199
|
-
<Input aria-label={t(M['ingestion.query'])} bind:value={query} />
|
|
200
|
-
<Button onclick={search} disabled={searching}>{t(M['ingestion.search'])}</Button>
|
|
201
|
-
{#if candidates}<ul>{#each candidates.items as candidate (candidate.key)}<li>{candidate.label} <Button disabled={!field} onclick={() => choose(candidate.id)}>{t(M['ingestion.choose'])}</Button></li>{/each}</ul>{#if candidates.truncated}<p>{t(M['ingestion.truncated'])}</p>{/if}{/if}
|
|
202
|
-
{/if}
|
|
201
|
+
{#if !action.plan && catalog}<CandidatePicker {host} {itemId} {catalog} {deny} onchoose={choose} />{/if}
|
|
203
202
|
{#if dirty}<p>{t(M['ingestion.reviewAgain'])}</p>{/if}
|
|
204
203
|
{#if invalid}<p role="alert">{t(M['ingestion.invalid'])}</p>{/if}
|
|
205
|
-
<div class="controls">{#if waiting}<Button disabled={dirty} onclick={() => decide('approve')}>{t(M['ingestion.approve'])}</Button>{/if}{#if !action.plan}<Button disabled={!dirty} onclick={saveEdits}>{t(M['ingestion.correct'])}</Button>{/if}{#if waiting}<Button onclick={() => decide('reject')}>{t(M['ingestion.reject'])}</Button
|
|
204
|
+
<div class="controls">{#if waiting && !resolved}<Button disabled={dirty} onclick={() => decide('approve')}>{t(M['ingestion.approve'])}</Button>{/if}{#if !action.plan && !resolved}<Button disabled={!dirty} onclick={saveEdits}>{t(M['ingestion.correct'])}</Button>{/if}{#if waiting}<Button onclick={() => decide('reject')}>{t(M['ingestion.reject'])}</Button>{#if !resolved}<Button onclick={() => decide('defer')}>{t(M['ingestion.defer'])}</Button>{/if}{/if}</div>
|
|
206
205
|
{/if}
|
|
207
|
-
{#if action.plan?.stepIndex === 0 && action.plan.args && host.editPlan && planEditable}
|
|
206
|
+
{#if action.plan?.stepIndex === 0 && action.plan.args && host.editPlan && planEditable && !resolved}
|
|
208
207
|
<Textarea aria-label={t(M['ingestion.planArgs'])} bind:value={planArgs} rows={8} />
|
|
209
208
|
{#if !editable && dirty}<p>{t(M['ingestion.reviewAgain'])}</p>{/if}
|
|
210
209
|
{#if !editable && invalid}<p role="alert">{t(M['ingestion.invalid'])}</p>{/if}
|
|
211
210
|
<Button onclick={editPlan}>{t(M['ingestion.editPlan'])}</Button>
|
|
212
211
|
{/if}
|
|
213
|
-
{#if action.review.state === 'authorized'}<Button disabled={
|
|
212
|
+
{#if action.review.state === 'authorized' && !resolved}<Button disabled={unsent} onclick={() => { if (!unsent) void run(() => host.apply(action.review.actionId), 'apply'); }}>{t(M['ingestion.apply'])}</Button>{/if}
|
|
214
213
|
{/if}
|
|
215
|
-
{#if action.review.state === 'stale' && !action.stalePlan && action.attemptId && host.editAction}
|
|
214
|
+
{#if action.review.state === 'stale' && !action.stalePlan && action.attemptId && host.editAction && !resolved}
|
|
216
215
|
<p>{t(M['ingestion.reviewAgain'])}</p>
|
|
217
216
|
<Textarea aria-label={t(M['ingestion.args'])} bind:value={args} rows={8} />
|
|
218
217
|
{#if invalid}<p role="alert">{t(M['ingestion.invalid'])}</p>{/if}
|
|
219
218
|
<Button onclick={saveEdits}>{t(M['ingestion.correct'])}</Button>
|
|
220
219
|
{/if}
|
|
221
|
-
{#if action.review.state === 'stale' && action.stalePlan && currentAttemptId && host.editPlan}
|
|
220
|
+
{#if action.review.state === 'stale' && action.stalePlan && currentAttemptId && host.editPlan && !resolved}
|
|
222
221
|
<p>{t(M['ingestion.reviewAgain'])}</p>
|
|
223
222
|
<Textarea aria-label={t(M['ingestion.planArgs'])} bind:value={planArgs} rows={8} />
|
|
224
223
|
{#if invalid}<p role="alert">{t(M['ingestion.invalid'])}</p>{/if}
|
|
225
224
|
<Button onclick={editPlan}>{t(M['ingestion.editPlan'])}</Button>
|
|
226
225
|
{/if}
|
|
227
226
|
{#if action.result}<h4>{t(M['ingestion.result'])}</h4><pre>{JSON.stringify(action.result,null,2)}</pre>{/if}
|
|
228
|
-
{#if host.feedback && action.args !== undefined}<fieldset><legend>{t(M['ingestion.feedback'])}</legend><Textarea aria-label={t(M['ingestion.comment'])} bind:value={comment} /><Button disabled={dirty} onclick={() => run(() => host.feedback?.({itemId,actionId:action.review.actionId,expectedRevision:action.review.revision,expectedReviewVersion:action.review.reviewVersion,bindingHash:action.review.bindingHash,judgment:'correct',comment,requestId:crypto.randomUUID()}) ?? Promise.resolve())}>{t(M['ingestion.correctJudgment'])}</Button><Button disabled={dirty} onclick={() => run(() => host.feedback?.({itemId,actionId:action.review.actionId,expectedRevision:action.review.revision,expectedReviewVersion:action.review.reviewVersion,bindingHash:action.review.bindingHash,judgment:'incorrect',comment,requestId:crypto.randomUUID()}) ?? Promise.resolve())}>{t(M['ingestion.incorrectJudgment'])}</Button></fieldset>{/if}
|
|
227
|
+
{#if host.feedback && action.args !== undefined && action.origin !== 'reviewer-selected'}<fieldset><legend>{t(M['ingestion.feedback'])}</legend><Textarea aria-label={t(M['ingestion.comment'])} bind:value={comment} /><Button disabled={dirty} onclick={() => run(() => host.feedback?.({itemId,actionId:action.review.actionId,expectedRevision:action.review.revision,expectedReviewVersion:action.review.reviewVersion,bindingHash:action.review.bindingHash,judgment:'correct',comment,requestId:crypto.randomUUID()}) ?? Promise.resolve())}>{t(M['ingestion.correctJudgment'])}</Button><Button disabled={dirty} onclick={() => run(() => host.feedback?.({itemId,actionId:action.review.actionId,expectedRevision:action.review.revision,expectedReviewVersion:action.review.reviewVersion,bindingHash:action.review.bindingHash,judgment:'incorrect',comment,requestId:crypto.randomUUID()}) ?? Promise.resolve())}>{t(M['ingestion.incorrectJudgment'])}</Button></fieldset>{/if}
|
|
229
228
|
</article>
|
|
230
229
|
<style>
|
|
231
230
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** A reviewer operation whose result may finish the item for this reviewer. */
|
|
2
|
+
export type ReviewOutcome = 'approve' | 'apply' | 'resolve';
|
|
1
3
|
import type { ProposalCatalogEntry } from '../../proposal-dto.js';
|
|
2
4
|
import type { IntakeReviewHost, ReviewAction } from '../../review-dto.js';
|
|
3
5
|
export interface Props {
|
|
@@ -6,8 +8,13 @@ export interface Props {
|
|
|
6
8
|
action: ReviewAction;
|
|
7
9
|
catalog?: ProposalCatalogEntry;
|
|
8
10
|
currentAttemptId?: string;
|
|
9
|
-
|
|
11
|
+
/** A resolved item is terminal: approve/correct/defer/apply and edits are hidden; only reject remains. */
|
|
12
|
+
resolved?: boolean;
|
|
13
|
+
/** `outcome` names an approve or apply so the parent can report it after its reload. */
|
|
14
|
+
run: (operation: () => Promise<unknown>, outcome?: ReviewOutcome) => Promise<void>;
|
|
10
15
|
deny: () => void;
|
|
16
|
+
/** Whether this editor holds unsent input (arguments, reason, comment). */
|
|
17
|
+
ondirty?: (dirty: boolean) => void;
|
|
11
18
|
}
|
|
12
19
|
declare const ActionReview: import("svelte").Component<Props, {}, "">;
|
|
13
20
|
type ActionReview = ReturnType<typeof ActionReview>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActionReview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ActionReview.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ActionReview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ActionReview.svelte.ts"],"names":[],"mappings":"AAGA,+EAA+E;AAC/E,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;AAQ5D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAClE,OAAO,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAK1E,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,0GAA0G;IAC1G,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wFAAwF;IACxF,GAAG,EAAE,CACH,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,EACjC,OAAO,CAAC,EAAE,aAAa,KACpB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAsND,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Input, Select } from '@happyvertical/smrt-ui/forms';
|
|
3
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
4
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
5
|
+
import { onDestroy } from 'svelte';
|
|
6
|
+
import type {
|
|
7
|
+
CandidatePage,
|
|
8
|
+
ProposalCatalogEntry,
|
|
9
|
+
} from '../../proposal-dto.js';
|
|
10
|
+
import type { IntakeReviewHost } from '../../review-dto.js';
|
|
11
|
+
import { M } from '../i18n.js';
|
|
12
|
+
export interface Props {
|
|
13
|
+
host: IntakeReviewHost;
|
|
14
|
+
itemId: string;
|
|
15
|
+
catalog: ProposalCatalogEntry;
|
|
16
|
+
/** A failed search fails closed: the owning view discards its payloads. */
|
|
17
|
+
deny: () => void;
|
|
18
|
+
/** Called only with a declared candidate field and an id from the current authorized page. */
|
|
19
|
+
onchoose: (field: string, id: string) => void;
|
|
20
|
+
}
|
|
21
|
+
let { host, itemId, catalog, deny, onchoose }: Props = $props();
|
|
22
|
+
const { t } = useI18n();
|
|
23
|
+
let query = $state(''),
|
|
24
|
+
field = $state(''),
|
|
25
|
+
candidates = $state<CandidatePage>(),
|
|
26
|
+
searching = $state(false);
|
|
27
|
+
let alive = true;
|
|
28
|
+
onDestroy(() => {
|
|
29
|
+
alive = false;
|
|
30
|
+
});
|
|
31
|
+
const fields = $derived(
|
|
32
|
+
Object.entries(catalog.references)
|
|
33
|
+
.filter(([, ref]) => ref.kind === 'candidate')
|
|
34
|
+
.map(([name]) => name),
|
|
35
|
+
);
|
|
36
|
+
async function search() {
|
|
37
|
+
if (searching) return;
|
|
38
|
+
searching = true;
|
|
39
|
+
candidates = undefined;
|
|
40
|
+
try {
|
|
41
|
+
const result = await host.candidates({
|
|
42
|
+
itemId,
|
|
43
|
+
handlerId: catalog.id,
|
|
44
|
+
handlerVersion: catalog.version,
|
|
45
|
+
query,
|
|
46
|
+
});
|
|
47
|
+
if (alive) candidates = result;
|
|
48
|
+
} catch {
|
|
49
|
+
if (alive) deny();
|
|
50
|
+
} finally {
|
|
51
|
+
if (alive) searching = false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function choose(id: string) {
|
|
55
|
+
const ref = catalog.references[field];
|
|
56
|
+
if (
|
|
57
|
+
ref?.kind !== 'candidate' ||
|
|
58
|
+
!candidates?.items.some(
|
|
59
|
+
(candidate) => candidate.id === id && candidate.model === ref.model,
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
return;
|
|
63
|
+
onchoose(field, id);
|
|
64
|
+
}
|
|
65
|
+
</script>
|
|
66
|
+
{#if fields.length}
|
|
67
|
+
<h4>{t(M['ingestion.candidates'])}</h4>
|
|
68
|
+
<Select aria-label={t(M['ingestion.field'])} bind:value={field}><option value="">{t(M['ingestion.field'])}</option>{#each fields as name (name)}<option value={name}>{name}</option>{/each}</Select>
|
|
69
|
+
<Input aria-label={t(M['ingestion.query'])} bind:value={query} />
|
|
70
|
+
<Button onclick={search} disabled={searching}>{t(M['ingestion.search'])}</Button>
|
|
71
|
+
{#if candidates}<ul>{#each candidates.items as candidate (candidate.key)}<li>{candidate.label} <Button disabled={!field} onclick={() => choose(candidate.id)}>{t(M['ingestion.choose'])}</Button></li>{/each}</ul>{#if candidates.truncated}<p>{t(M['ingestion.truncated'])}</p>{/if}{/if}
|
|
72
|
+
{/if}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ProposalCatalogEntry } from '../../proposal-dto.js';
|
|
2
|
+
import type { IntakeReviewHost } from '../../review-dto.js';
|
|
3
|
+
export interface Props {
|
|
4
|
+
host: IntakeReviewHost;
|
|
5
|
+
itemId: string;
|
|
6
|
+
catalog: ProposalCatalogEntry;
|
|
7
|
+
/** A failed search fails closed: the owning view discards its payloads. */
|
|
8
|
+
deny: () => void;
|
|
9
|
+
/** Called only with a declared candidate field and an id from the current authorized page. */
|
|
10
|
+
onchoose: (field: string, id: string) => void;
|
|
11
|
+
}
|
|
12
|
+
declare const CandidatePicker: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type CandidatePicker = ReturnType<typeof CandidatePicker>;
|
|
14
|
+
export default CandidatePicker;
|
|
15
|
+
//# sourceMappingURL=CandidatePicker.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CandidatePicker.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/CandidatePicker.svelte.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAEV,oBAAoB,EACrB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,oBAAoB,CAAC;IAC9B,2EAA2E;IAC3E,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,8FAA8F;IAC9F,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAiED,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|
|
@@ -6,19 +6,75 @@ import { M } from '../i18n.js';
|
|
|
6
6
|
import { evidenceUrl } from '../view-utils.js';
|
|
7
7
|
export interface Props {
|
|
8
8
|
evidence: EvidenceView[];
|
|
9
|
+
/**
|
|
10
|
+
* Highest observed page per evidence id. A PDF with more than one observed
|
|
11
|
+
* page gets inline page navigation (the same-origin URL plus `#page=n`).
|
|
12
|
+
*/
|
|
13
|
+
pages?: Record<string, number>;
|
|
14
|
+
/** `stack` lists every part; `pager` shows one part at a time with navigation. */
|
|
15
|
+
layout?: 'stack' | 'pager';
|
|
16
|
+
/** Bindable: the part the pager shows, by evidence id. */
|
|
17
|
+
chosen?: string;
|
|
18
|
+
/** Bindable: page per evidence id; absent until the reviewer navigates. */
|
|
19
|
+
page?: Record<string, number>;
|
|
9
20
|
}
|
|
10
|
-
let {
|
|
21
|
+
let {
|
|
22
|
+
evidence,
|
|
23
|
+
pages = {},
|
|
24
|
+
layout = 'stack',
|
|
25
|
+
// Bindable so a parent can keep them while this viewer remounts on reload.
|
|
26
|
+
chosen = $bindable(),
|
|
27
|
+
page = $bindable({}),
|
|
28
|
+
}: Props = $props();
|
|
11
29
|
const { t } = useI18n();
|
|
30
|
+
const inline = (view: EvidenceView) =>
|
|
31
|
+
view.evidence.mediaType.startsWith('image/') ||
|
|
32
|
+
view.evidence.mediaType === 'application/pdf' ||
|
|
33
|
+
view.evidence.mediaType.startsWith('audio/');
|
|
34
|
+
// The pager opens on the first part that renders inline (the uploaded file,
|
|
35
|
+
// not its capture record); the reviewer can step to every other part.
|
|
36
|
+
const shown = $derived.by(() => {
|
|
37
|
+
if (layout !== 'pager') return evidence;
|
|
38
|
+
const current =
|
|
39
|
+
evidence.find((view) => view.evidence.id === chosen) ??
|
|
40
|
+
evidence.find((view) => inline(view) && evidenceUrl(view.viewUrl)) ??
|
|
41
|
+
evidence[0];
|
|
42
|
+
return current ? [current] : [];
|
|
43
|
+
});
|
|
44
|
+
const position = $derived(shown.length === 1 ? evidence.indexOf(shown[0]) : -1);
|
|
45
|
+
function step(offset: number) {
|
|
46
|
+
const next = evidence[position + offset];
|
|
47
|
+
if (next) chosen = next.evidence.id;
|
|
48
|
+
}
|
|
49
|
+
function pageUrl(url: string, id: string) {
|
|
50
|
+
const current = page[id];
|
|
51
|
+
return current === undefined ? url : `${url.split('#')[0]}#page=${current}`;
|
|
52
|
+
}
|
|
53
|
+
function turn(id: string, offset: number) {
|
|
54
|
+
const count = pages[id] ?? 1;
|
|
55
|
+
page = {
|
|
56
|
+
...page,
|
|
57
|
+
[id]: Math.min(count, Math.max(1, (page[id] ?? 1) + offset)),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
12
60
|
</script>
|
|
13
61
|
<section data-testid="evidence-viewer" aria-label={t(M['ingestion.evidence'])} data-smrt-component="EvidenceViewer">
|
|
14
62
|
<h2>{t(M['ingestion.evidence'])}</h2>
|
|
15
|
-
{#
|
|
63
|
+
{#if layout === 'pager' && evidence.length > 1 && position >= 0}
|
|
64
|
+
<div class="pager" data-testid="evidence-pager"><Button disabled={position <= 0} onclick={() => step(-1)}>{t(M['ingestion.previousOriginal'])}</Button><p role="status">{t(M['ingestion.originalOf'], { index: position + 1, count: evidence.length })}</p><Button disabled={position >= evidence.length - 1} onclick={() => step(1)}>{t(M['ingestion.nextOriginal'])}</Button></div>
|
|
65
|
+
{/if}
|
|
66
|
+
{#each shown as view (view.evidence.id)}
|
|
16
67
|
{@const url = evidenceUrl(view.viewUrl)}
|
|
68
|
+
{@const count = pages[view.evidence.id] ?? 0}
|
|
17
69
|
<article id={`evidence-${view.evidence.id}`}>
|
|
18
70
|
<h3>{view.label}</h3>
|
|
19
71
|
{#if url}
|
|
20
72
|
{#if view.evidence.mediaType.startsWith('image/')}<img src={url} alt={view.label} />
|
|
21
|
-
{:else if view.evidence.mediaType === 'application/pdf'}
|
|
73
|
+
{:else if view.evidence.mediaType === 'application/pdf'}
|
|
74
|
+
{@const src = pageUrl(url, view.evidence.id)}
|
|
75
|
+
{#if count > 1}{@const current = page[view.evidence.id] ?? 1}<div class="pager" data-testid="page-pager"><Button disabled={current <= 1} onclick={() => turn(view.evidence.id, -1)}>{t(M['ingestion.previousPage'])}</Button><p role="status">{t(M['ingestion.pageOf'], { page: current, count })}</p><Button disabled={current >= count} onclick={() => turn(view.evidence.id, 1)}>{t(M['ingestion.nextPage'])}</Button></div>{/if}
|
|
76
|
+
<!-- A fragment-only change does not reliably move an embedded PDF viewer; a new frame does. -->
|
|
77
|
+
{#key src}<iframe {src} title={view.label}></iframe>{/key}
|
|
22
78
|
{:else if view.evidence.mediaType.startsWith('audio/')}<audio controls src={url} aria-label={view.label}></audio>{/if}
|
|
23
79
|
<Button href={url} target="_blank" rel="noopener noreferrer">{t(M['ingestion.original'])}</Button>
|
|
24
80
|
{:else}<p>{t(M['ingestion.noOriginal'])}</p>{/if}
|
|
@@ -29,6 +85,6 @@ const { t } = useI18n();
|
|
|
29
85
|
<style>
|
|
30
86
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
31
87
|
@layer smrt.theme {
|
|
32
|
-
section,article{min-width:0}article{margin-block:1rem}img,iframe,audio{display:block;max-width:100%;width:100%;margin-block:.5rem}img{object-fit:contain;max-height:32rem}iframe{height:32rem;border:var(--smrt-hairline)}pre{white-space:pre-wrap;overflow-wrap:anywhere}
|
|
88
|
+
section,article{min-width:0}article{margin-block:1rem}img,iframe,audio{display:block;max-width:100%;width:100%;margin-block:.5rem}img{object-fit:contain;max-height:32rem}iframe{height:32rem;border:var(--smrt-hairline)}pre{white-space:pre-wrap;overflow-wrap:anywhere}.pager{display:flex;flex-wrap:wrap;align-items:center;gap:.5rem}.pager p{margin:0}
|
|
33
89
|
}
|
|
34
90
|
</style>
|
|
@@ -1,8 +1,19 @@
|
|
|
1
1
|
import type { EvidenceView } from '../../review-dto.js';
|
|
2
2
|
export interface Props {
|
|
3
3
|
evidence: EvidenceView[];
|
|
4
|
+
/**
|
|
5
|
+
* Highest observed page per evidence id. A PDF with more than one observed
|
|
6
|
+
* page gets inline page navigation (the same-origin URL plus `#page=n`).
|
|
7
|
+
*/
|
|
8
|
+
pages?: Record<string, number>;
|
|
9
|
+
/** `stack` lists every part; `pager` shows one part at a time with navigation. */
|
|
10
|
+
layout?: 'stack' | 'pager';
|
|
11
|
+
/** Bindable: the part the pager shows, by evidence id. */
|
|
12
|
+
chosen?: string;
|
|
13
|
+
/** Bindable: page per evidence id; absent until the reviewer navigates. */
|
|
14
|
+
page?: Record<string, number>;
|
|
4
15
|
}
|
|
5
|
-
declare const EvidenceViewer: import("svelte").Component<Props, {}, "">;
|
|
16
|
+
declare const EvidenceViewer: import("svelte").Component<Props, {}, "page" | "chosen">;
|
|
6
17
|
type EvidenceViewer = ReturnType<typeof EvidenceViewer>;
|
|
7
18
|
export default EvidenceViewer;
|
|
8
19
|
//# sourceMappingURL=EvidenceViewer.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EvidenceViewer.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EvidenceViewer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,YAAY,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"EvidenceViewer.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/EvidenceViewer.svelte.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAIxD,MAAM,WAAW,KAAK;IACpB,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,kFAAkF;IAClF,MAAM,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC;IAC3B,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AA4ED,QAAA,MAAM,cAAc,0DAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|