@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
|
@@ -2,18 +2,56 @@
|
|
|
2
2
|
import { FilePicker, Input, Select } from '@happyvertical/smrt-ui/forms';
|
|
3
3
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
4
4
|
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
5
|
-
import { onDestroy, onMount } from 'svelte';
|
|
5
|
+
import { onDestroy, onMount, type Snippet } from 'svelte';
|
|
6
6
|
import type {
|
|
7
7
|
InboxEntry,
|
|
8
8
|
InboxState,
|
|
9
9
|
IntakeReviewHost,
|
|
10
10
|
} from '../../review-dto.js';
|
|
11
11
|
import { M } from '../i18n.js';
|
|
12
|
+
import { evidenceUrl } from '../view-utils.js';
|
|
12
13
|
export interface Props {
|
|
13
14
|
host: IntakeReviewHost;
|
|
14
15
|
onselect?: (itemId: string) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Optional per-row aside; without it a row shows the host's original
|
|
18
|
+
* link/preview. Rendered in a plain `div`, not an `aside` landmark, so a
|
|
19
|
+
* page of rows does not add one complementary landmark per row.
|
|
20
|
+
*/
|
|
21
|
+
rowAside?: Snippet<[InboxEntry]>;
|
|
22
|
+
/** The row a workspace has open; marked `aria-current`. */
|
|
23
|
+
selectedId?: string;
|
|
24
|
+
/**
|
|
25
|
+
* The displayed rows after every settled load or page append (not after
|
|
26
|
+
* `patch`, which returns them), whether more pages exist, and the entries
|
|
27
|
+
* exactly as the page read returned them (`appended` for Load more), absent
|
|
28
|
+
* when the read failed.
|
|
29
|
+
*/
|
|
30
|
+
onitems?: (
|
|
31
|
+
items: InboxEntry[],
|
|
32
|
+
more: boolean,
|
|
33
|
+
page?: { entries: InboxEntry[]; appended: boolean; issued?: number },
|
|
34
|
+
) => void;
|
|
35
|
+
/** Called before a page request so a workspace can order concurrent follow reads. */
|
|
36
|
+
onreadstart?: () => void;
|
|
37
|
+
/** A row's status text; the listed inbox state is shown when omitted or undefined. */
|
|
38
|
+
status?: (entry: InboxEntry) => string | undefined;
|
|
39
|
+
/** Show the per-row "Handled, no action" control (a workspace resolves in its item pane). */
|
|
40
|
+
resolveInRow?: boolean;
|
|
41
|
+
/** Numbers each page read when its request starts (`issued` on the page). */
|
|
42
|
+
stamp?: () => number;
|
|
15
43
|
}
|
|
16
|
-
let {
|
|
44
|
+
let {
|
|
45
|
+
host,
|
|
46
|
+
onselect,
|
|
47
|
+
rowAside,
|
|
48
|
+
selectedId,
|
|
49
|
+
onitems,
|
|
50
|
+
onreadstart,
|
|
51
|
+
status,
|
|
52
|
+
resolveInRow = true,
|
|
53
|
+
stamp,
|
|
54
|
+
}: Props = $props();
|
|
17
55
|
const { t } = useI18n();
|
|
18
56
|
const states: InboxState[] = [
|
|
19
57
|
'processing',
|
|
@@ -23,11 +61,13 @@ const states: InboxState[] = [
|
|
|
23
61
|
'deferred',
|
|
24
62
|
'partially_completed',
|
|
25
63
|
'completed',
|
|
64
|
+
'resolved',
|
|
26
65
|
];
|
|
27
66
|
let items = $state<InboxEntry[]>([]),
|
|
28
67
|
selectedState = $state(''),
|
|
29
68
|
assignee = $state(''),
|
|
30
69
|
assignments = $state<Record<string, string>>({}),
|
|
70
|
+
reasons = $state<Record<string, string>>({}),
|
|
31
71
|
files = $state<File[]>([]),
|
|
32
72
|
cursor = $state<string>(),
|
|
33
73
|
pending = $state(false),
|
|
@@ -35,41 +75,145 @@ let items = $state<InboxEntry[]>([]),
|
|
|
35
75
|
let alive = true,
|
|
36
76
|
epoch = 0;
|
|
37
77
|
let uploadRequestId: string | undefined;
|
|
78
|
+
// The filter the displayed rows were listed under (the inputs may have been
|
|
79
|
+
// edited since); `patch` keeps a row only while it still matches it.
|
|
80
|
+
let listed: { state?: InboxState; assigneeId?: string } = {};
|
|
81
|
+
function publish(page?: {
|
|
82
|
+
entries: InboxEntry[];
|
|
83
|
+
appended: boolean;
|
|
84
|
+
issued?: number;
|
|
85
|
+
}) {
|
|
86
|
+
onitems?.($state.snapshot(items) as InboxEntry[], !!cursor, page);
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Replace one displayed row with a fresher entry for the same item (item and
|
|
90
|
+
* state; the assignment only when its version is newer), keeping its listed
|
|
91
|
+
* label and preview: `load` entries
|
|
92
|
+
* carry no preview and a host may label them differently. Unless `keep` is
|
|
93
|
+
* set, a row that no longer matches the listed filter leaves the list. Rows
|
|
94
|
+
* that are not displayed are never added. Returns the displayed rows.
|
|
95
|
+
*/
|
|
96
|
+
export function patch(entry: InboxEntry, keep = false): InboxEntry[] {
|
|
97
|
+
const index = items.findIndex((row) => row.item.id === entry.item.id);
|
|
98
|
+
if (index < 0) return $state.snapshot(items) as InboxEntry[];
|
|
99
|
+
const previous = items[index];
|
|
100
|
+
// Assignment is versioned (the host's compare-and-set): an entry carrying
|
|
101
|
+
// an older or equal version never replaces the row's, so a view loaded
|
|
102
|
+
// before an assignment cannot roll the row (and the next expected version)
|
|
103
|
+
// back.
|
|
104
|
+
const newer = entry.assignment.version > previous.assignment.version;
|
|
105
|
+
const assignment = newer ? entry.assignment : previous.assignment;
|
|
106
|
+
const matches =
|
|
107
|
+
(!listed.state || entry.state === listed.state) &&
|
|
108
|
+
(!listed.assigneeId || assignment.assigneeId === listed.assigneeId);
|
|
109
|
+
if (keep || matches) {
|
|
110
|
+
const evidence = previous.evidence;
|
|
111
|
+
items[index] = {
|
|
112
|
+
item: entry.item,
|
|
113
|
+
label: previous.label,
|
|
114
|
+
state: entry.state,
|
|
115
|
+
assignment: $state.snapshot(assignment),
|
|
116
|
+
...(evidence ? { evidence } : {}),
|
|
117
|
+
};
|
|
118
|
+
// An unsaved assignee the reviewer typed survives unless the durable
|
|
119
|
+
// assignment itself changed.
|
|
120
|
+
if (newer) assignments[entry.item.id] = assignment.assigneeId ?? '';
|
|
121
|
+
} else items = items.filter((row) => row.item.id !== entry.item.id);
|
|
122
|
+
return $state.snapshot(items) as InboxEntry[];
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Append the next page, if any. Resolves with the displayed rows and what
|
|
126
|
+
* happened: `appended` (or `none` when there is no further page), `pending`
|
|
127
|
+
* when another read was in flight or superseded this one (nothing was
|
|
128
|
+
* appended), or `failed` when the page read failed (the list failed closed
|
|
129
|
+
* and its rows were cleared).
|
|
130
|
+
*/
|
|
131
|
+
export async function more(): Promise<{
|
|
132
|
+
items: InboxEntry[];
|
|
133
|
+
result: 'appended' | 'none' | 'pending' | 'failed';
|
|
134
|
+
}> {
|
|
135
|
+
let result: 'appended' | 'none' | 'pending' | 'failed';
|
|
136
|
+
if (pending) result = 'pending';
|
|
137
|
+
else if (!cursor) result = 'none';
|
|
138
|
+
else {
|
|
139
|
+
const read = await load(true);
|
|
140
|
+
result =
|
|
141
|
+
read === 'shown' ? 'appended' : read === 'failed' ? 'failed' : 'pending';
|
|
142
|
+
}
|
|
143
|
+
return { items: $state.snapshot(items) as InboxEntry[], result };
|
|
144
|
+
}
|
|
38
145
|
onDestroy(() => {
|
|
39
146
|
alive = false;
|
|
40
147
|
epoch++;
|
|
41
148
|
files = [];
|
|
42
149
|
uploadRequestId = undefined;
|
|
43
150
|
});
|
|
44
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Resolves `shown` when this read's page was displayed, `failed` when it
|
|
153
|
+
* failed (the rows were cleared), `superseded` when a newer read or the
|
|
154
|
+
* component's end fenced it.
|
|
155
|
+
*/
|
|
156
|
+
async function load(more = false): Promise<'shown' | 'failed' | 'superseded'> {
|
|
45
157
|
const ticket = ++epoch;
|
|
158
|
+
onreadstart?.();
|
|
46
159
|
pending = true;
|
|
47
160
|
error = false;
|
|
48
|
-
|
|
161
|
+
let page:
|
|
162
|
+
| { entries: InboxEntry[]; appended: boolean; issued?: number }
|
|
163
|
+
| undefined;
|
|
49
164
|
const next = more ? cursor : undefined;
|
|
50
|
-
|
|
51
|
-
|
|
165
|
+
// The displayed rows stay (controls disabled while pending) until the new
|
|
166
|
+
// page arrives, so a workspace can keep patching and navigating them; a
|
|
167
|
+
// failed read still clears them.
|
|
168
|
+
const filter = more
|
|
169
|
+
? listed
|
|
170
|
+
: {
|
|
171
|
+
state: selectedState ? (selectedState as InboxState) : undefined,
|
|
172
|
+
assigneeId: assignee || undefined,
|
|
173
|
+
};
|
|
52
174
|
try {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
175
|
+
const issued = stamp?.();
|
|
176
|
+
const read = await host.list({ ...filter, cursor: next });
|
|
177
|
+
if (!alive || ticket !== epoch) return 'superseded';
|
|
178
|
+
listed = filter;
|
|
179
|
+
// An appended page builds on the rows as displayed now (patches made
|
|
180
|
+
// while it loaded included); a row already shown is not repeated.
|
|
181
|
+
const shown = new Set(more ? items.map((entry) => entry.item.id) : []);
|
|
182
|
+
const added = read.items.filter((entry) => !shown.has(entry.item.id));
|
|
183
|
+
page = {
|
|
184
|
+
entries: $state.snapshot(more ? added : read.items) as InboxEntry[],
|
|
185
|
+
appended: more,
|
|
186
|
+
...(issued !== undefined ? { issued } : {}),
|
|
187
|
+
};
|
|
188
|
+
const kept = more ? { assignments, reasons } : undefined;
|
|
189
|
+
items = more ? [...items, ...added] : read.items;
|
|
190
|
+
cursor = read.nextCursor;
|
|
61
191
|
assignments = Object.fromEntries(
|
|
62
|
-
items.map((entry) => [
|
|
192
|
+
items.map((entry) => [
|
|
193
|
+
entry.item.id,
|
|
194
|
+
kept?.assignments[entry.item.id] ?? entry.assignment.assigneeId ?? '',
|
|
195
|
+
]),
|
|
196
|
+
);
|
|
197
|
+
reasons = Object.fromEntries(
|
|
198
|
+
items.map((entry) => [entry.item.id, kept?.reasons[entry.item.id] ?? '']),
|
|
63
199
|
);
|
|
64
200
|
} catch {
|
|
201
|
+
// Any failed read, Load more included, fails closed: the host cannot say
|
|
202
|
+
// whether it was transient or a revoked grant.
|
|
65
203
|
if (alive && ticket === epoch) {
|
|
66
204
|
items = [];
|
|
205
|
+
assignments = {};
|
|
206
|
+
reasons = {};
|
|
67
207
|
cursor = undefined;
|
|
68
208
|
error = true;
|
|
69
209
|
}
|
|
70
210
|
} finally {
|
|
71
|
-
if (alive && ticket === epoch)
|
|
211
|
+
if (alive && ticket === epoch) {
|
|
212
|
+
pending = false;
|
|
213
|
+
publish(page);
|
|
214
|
+
}
|
|
72
215
|
}
|
|
216
|
+
return page ? 'shown' : alive && ticket === epoch ? 'failed' : 'superseded';
|
|
73
217
|
}
|
|
74
218
|
async function mutate(operation: () => Promise<unknown>, selected = false) {
|
|
75
219
|
if (pending) return;
|
|
@@ -92,6 +236,7 @@ async function mutate(operation: () => Promise<unknown>, selected = false) {
|
|
|
92
236
|
assignments = {};
|
|
93
237
|
error = true;
|
|
94
238
|
pending = false;
|
|
239
|
+
publish();
|
|
95
240
|
}
|
|
96
241
|
}
|
|
97
242
|
}
|
|
@@ -119,17 +264,25 @@ onMount(() => {
|
|
|
119
264
|
{#if error}<p role="alert">{t(M['ingestion.error'])}</p>{/if}
|
|
120
265
|
{#if pending}<p role="status">{t(M['ingestion.loading'])}</p>{/if}
|
|
121
266
|
{#if !pending && !error && !items.length}<p>{t(M['ingestion.empty'])}</p>{/if}
|
|
122
|
-
<ul>{#each items as entry (entry.item.id)}<li>
|
|
123
|
-
<h2>{entry.label}</h2><p>{t(M[`ingestion.${entry.state}`])}</p>
|
|
267
|
+
<ul>{#each items as entry (entry.item.id)}<li data-item-id={entry.item.id} aria-current={entry.item.id === selectedId ? 'true' : undefined} class:selected={entry.item.id === selectedId}>
|
|
268
|
+
<h2>{entry.label}</h2><p data-testid="row-status">{status?.(entry) ?? t(M[`ingestion.${entry.state}`])}</p>
|
|
269
|
+
{#if rowAside}<div class="row-aside">{@render rowAside(entry)}</div>{:else if entry.evidence}{@const original = evidenceUrl(entry.evidence.viewUrl)}{@const thumbnail = evidenceUrl(entry.evidence.thumbnailUrl)}{#if thumbnail || original}<div class="row-aside">
|
|
270
|
+
{#if thumbnail}<img src={thumbnail} alt={entry.evidence.label} loading="lazy" />{/if}
|
|
271
|
+
{#if original}<a href={original} target="_blank" rel="noopener noreferrer" aria-label={`${t(M['ingestion.original'])}: ${entry.label}`}>{t(M['ingestion.original'])}</a>{/if}
|
|
272
|
+
</div>{/if}{/if}
|
|
124
273
|
<Button onclick={() => onselect?.(entry.item.id)} disabled={pending}>{t(M['ingestion.open'])}</Button>
|
|
125
274
|
<Input aria-label={`${t(M['ingestion.assignee'])}: ${entry.label}`} bind:value={assignments[entry.item.id]} disabled={pending} />
|
|
126
275
|
<Button disabled={pending} onclick={() => mutate(() => host.assign({itemId:entry.item.id,expectedVersion:entry.assignment.version,assigneeId:assignments[entry.item.id] || null,requestId:crypto.randomUUID()}))}>{t(M['ingestion.assign'])}</Button>
|
|
276
|
+
{#if resolveInRow && host.resolve && !entry.item.disposition}
|
|
277
|
+
<Input aria-label={`${t(M['ingestion.resolveReason'])}: ${entry.label}`} bind:value={reasons[entry.item.id]} disabled={pending} />
|
|
278
|
+
<Button disabled={pending} onclick={() => { const close = host.resolve; const reason = reasons[entry.item.id]; if (close) void mutate(() => close({itemId:entry.item.id,disposition:'resolve_no_action',requestId:crypto.randomUUID(),...(reason ? { reason } : {})})); }}>{t(M['ingestion.resolveNoAction'])}</Button>
|
|
279
|
+
{/if}
|
|
127
280
|
</li>{/each}</ul>
|
|
128
281
|
{#if cursor}<Button disabled={pending} onclick={() => load(true)}>{t(M['ingestion.more'])}</Button>{/if}
|
|
129
282
|
</section>
|
|
130
283
|
<style>
|
|
131
284
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
132
285
|
@layer smrt.theme {
|
|
133
|
-
section{max-width:72rem;margin-inline:auto;padding:1rem;color:var(--smrt-color-on-surface)}.toolbar{display:flex;flex-wrap:wrap;gap:.75rem;margin-block:1rem}ul{list-style:none;padding:0}li{padding:1rem;border-block-end:var(--smrt-hairline);display:grid;gap:.75rem}h2{font-size:1.1rem}
|
|
286
|
+
section{max-width:72rem;margin-inline:auto;padding:1rem;color:var(--smrt-color-on-surface)}.toolbar{display:flex;flex-wrap:wrap;gap:.75rem;margin-block:1rem}ul{list-style:none;padding:0}li{padding:1rem;border-block-end:var(--smrt-hairline);display:grid;gap:.75rem}li.selected{border-inline-start:.25rem solid var(--smrt-color-primary)}h2{font-size:1.1rem}.row-aside{display:flex;flex-wrap:wrap;align-items:center;gap:.75rem}.row-aside img{max-block-size:6rem;max-inline-size:100%;object-fit:contain}
|
|
134
287
|
}
|
|
135
288
|
</style>
|
|
@@ -1,9 +1,43 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Snippet } from 'svelte';
|
|
2
|
+
import type { InboxEntry, IntakeReviewHost } from '../../review-dto.js';
|
|
2
3
|
export interface Props {
|
|
3
4
|
host: IntakeReviewHost;
|
|
4
5
|
onselect?: (itemId: string) => void;
|
|
6
|
+
/**
|
|
7
|
+
* Optional per-row aside; without it a row shows the host's original
|
|
8
|
+
* link/preview. Rendered in a plain `div`, not an `aside` landmark, so a
|
|
9
|
+
* page of rows does not add one complementary landmark per row.
|
|
10
|
+
*/
|
|
11
|
+
rowAside?: Snippet<[InboxEntry]>;
|
|
12
|
+
/** The row a workspace has open; marked `aria-current`. */
|
|
13
|
+
selectedId?: string;
|
|
14
|
+
/**
|
|
15
|
+
* The displayed rows after every settled load or page append (not after
|
|
16
|
+
* `patch`, which returns them), whether more pages exist, and the entries
|
|
17
|
+
* exactly as the page read returned them (`appended` for Load more), absent
|
|
18
|
+
* when the read failed.
|
|
19
|
+
*/
|
|
20
|
+
onitems?: (items: InboxEntry[], more: boolean, page?: {
|
|
21
|
+
entries: InboxEntry[];
|
|
22
|
+
appended: boolean;
|
|
23
|
+
issued?: number;
|
|
24
|
+
}) => void;
|
|
25
|
+
/** Called before a page request so a workspace can order concurrent follow reads. */
|
|
26
|
+
onreadstart?: () => void;
|
|
27
|
+
/** A row's status text; the listed inbox state is shown when omitted or undefined. */
|
|
28
|
+
status?: (entry: InboxEntry) => string | undefined;
|
|
29
|
+
/** Show the per-row "Handled, no action" control (a workspace resolves in its item pane). */
|
|
30
|
+
resolveInRow?: boolean;
|
|
31
|
+
/** Numbers each page read when its request starts (`issued` on the page). */
|
|
32
|
+
stamp?: () => number;
|
|
5
33
|
}
|
|
6
|
-
declare const InboxView: import("svelte").Component<Props, {
|
|
34
|
+
declare const InboxView: import("svelte").Component<Props, {
|
|
35
|
+
patch: (entry: InboxEntry, keep?: boolean) => InboxEntry[];
|
|
36
|
+
more: () => Promise<{
|
|
37
|
+
items: InboxEntry[];
|
|
38
|
+
result: "appended" | "none" | "pending" | "failed";
|
|
39
|
+
}>;
|
|
40
|
+
}, "">;
|
|
7
41
|
type InboxView = ReturnType<typeof InboxView>;
|
|
8
42
|
export default InboxView;
|
|
9
43
|
//# sourceMappingURL=InboxView.svelte.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InboxView.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/InboxView.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"InboxView.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/InboxView.svelte.ts"],"names":[],"mappings":"AAMA,OAAO,EAAsB,KAAK,OAAO,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,KAAK,EACV,UAAU,EAEV,gBAAgB,EACjB,MAAM,qBAAqB,CAAC;AAI7B,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACjC,2DAA2D;IAC3D,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CACR,KAAK,EAAE,UAAU,EAAE,EACnB,IAAI,EAAE,OAAO,EACb,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,UAAU,EAAE,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KACjE,IAAI,CAAC;IACV,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,sFAAsF;IACtF,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,GAAG,SAAS,CAAC;IACnD,6FAA6F;IAC7F,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;CACtB;AA6PD,QAAA,MAAM,SAAS;mBA/LQ,UAAU,qBAAiB,UAAU,EAAE;gBAmCtC,OAAO,CAAC;QAC9B,KAAK,EAAE,UAAU,EAAE,CAAC;QACpB,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;KACpD,CAAC;MAyJqD,CAAC;AACxD,KAAK,SAAS,GAAG,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC;AAC9C,eAAe,SAAS,CAAC"}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
import type {
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { InboxEntry, IntakeReviewHost } from '../../review-dto.js';
|
|
3
4
|
import InboxView from './InboxView.svelte';
|
|
4
5
|
export interface Props {
|
|
5
6
|
host: IntakeReviewHost;
|
|
6
7
|
contextKey?: string;
|
|
7
8
|
onselect?: (itemId: string) => void;
|
|
9
|
+
/** Optional per-row aside (preview, original link); receives the page entry. */
|
|
10
|
+
rowAside?: Snippet<[InboxEntry]>;
|
|
8
11
|
}
|
|
9
|
-
let { host, contextKey = '', onselect }: Props = $props();
|
|
12
|
+
let { host, contextKey = '', onselect, rowAside }: Props = $props();
|
|
10
13
|
</script>
|
|
11
|
-
{#key host}{#key contextKey}<InboxView {host} {onselect} />{/key}{/key}
|
|
14
|
+
{#key host}{#key contextKey}<InboxView {host} {onselect} {rowAside} />{/key}{/key}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { InboxEntry, IntakeReviewHost } from '../../review-dto.js';
|
|
2
3
|
export interface Props {
|
|
3
4
|
host: IntakeReviewHost;
|
|
4
5
|
contextKey?: string;
|
|
5
6
|
onselect?: (itemId: string) => void;
|
|
7
|
+
/** Optional per-row aside (preview, original link); receives the page entry. */
|
|
8
|
+
rowAside?: Snippet<[InboxEntry]>;
|
|
6
9
|
}
|
|
7
10
|
declare const IntakeInbox: import("svelte").Component<Props, {}, "">;
|
|
8
11
|
type IntakeInbox = ReturnType<typeof IntakeInbox>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntakeInbox.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/IntakeInbox.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"IntakeInbox.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/IntakeInbox.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGxE,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;CAClC;AAYD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|
|
@@ -5,7 +5,9 @@ export interface Props {
|
|
|
5
5
|
host: IntakeReviewHost;
|
|
6
6
|
itemId: string;
|
|
7
7
|
contextKey?: string;
|
|
8
|
+
/** Reload interval while the host reports the item as processing. */
|
|
9
|
+
pollMs?: number;
|
|
8
10
|
}
|
|
9
|
-
let { host, itemId, contextKey = '' }: Props = $props();
|
|
11
|
+
let { host, itemId, contextKey = '', pollMs = 2000 }: Props = $props();
|
|
10
12
|
</script>
|
|
11
|
-
{#key host}{#key contextKey}{#key itemId}<ReviewView {host} {itemId} />{/key}{/key}{/key}
|
|
13
|
+
{#key host}{#key contextKey}{#key itemId}<ReviewView {host} {itemId} {pollMs} />{/key}{/key}{/key}
|
|
@@ -3,6 +3,8 @@ export interface Props {
|
|
|
3
3
|
host: IntakeReviewHost;
|
|
4
4
|
itemId: string;
|
|
5
5
|
contextKey?: string;
|
|
6
|
+
/** Reload interval while the host reports the item as processing. */
|
|
7
|
+
pollMs?: number;
|
|
6
8
|
}
|
|
7
9
|
declare const IntakeReview: import("svelte").Component<Props, {}, "">;
|
|
8
10
|
type IntakeReview = ReturnType<typeof IntakeReview>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IntakeReview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/IntakeReview.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"IntakeReview.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/IntakeReview.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAG5D,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAWD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
import type { InboxEntry, IntakeReviewHost } from '../../review-dto.js';
|
|
4
|
+
import TriageView from './TriageView.svelte';
|
|
5
|
+
export interface Props {
|
|
6
|
+
host: IntakeReviewHost;
|
|
7
|
+
/** Replace whenever the authenticated tenant/session context changes. */
|
|
8
|
+
contextKey?: string;
|
|
9
|
+
/** Item to open first; afterwards the workspace owns the selection. */
|
|
10
|
+
itemId?: string;
|
|
11
|
+
/** Reload interval while an item's background stage is in flight. */
|
|
12
|
+
pollMs?: number;
|
|
13
|
+
/** Told about every selection change (e.g. to keep a URL in step); never navigates. */
|
|
14
|
+
onselect?: (itemId: string | undefined) => void;
|
|
15
|
+
/** Optional per-row aside (preview, original link); receives the page entry. */
|
|
16
|
+
rowAside?: Snippet<[InboxEntry]>;
|
|
17
|
+
}
|
|
18
|
+
let {
|
|
19
|
+
host,
|
|
20
|
+
contextKey = '',
|
|
21
|
+
itemId,
|
|
22
|
+
pollMs = 2000,
|
|
23
|
+
onselect,
|
|
24
|
+
rowAside,
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
</script>
|
|
27
|
+
{#key host}{#key contextKey}<TriageView {host} {itemId} {pollMs} {onselect} {rowAside} />{/key}{/key}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import type { InboxEntry, IntakeReviewHost } from '../../review-dto.js';
|
|
3
|
+
export interface Props {
|
|
4
|
+
host: IntakeReviewHost;
|
|
5
|
+
/** Replace whenever the authenticated tenant/session context changes. */
|
|
6
|
+
contextKey?: string;
|
|
7
|
+
/** Item to open first; afterwards the workspace owns the selection. */
|
|
8
|
+
itemId?: string;
|
|
9
|
+
/** Reload interval while an item's background stage is in flight. */
|
|
10
|
+
pollMs?: number;
|
|
11
|
+
/** Told about every selection change (e.g. to keep a URL in step); never navigates. */
|
|
12
|
+
onselect?: (itemId: string | undefined) => void;
|
|
13
|
+
/** Optional per-row aside (preview, original link); receives the page entry. */
|
|
14
|
+
rowAside?: Snippet<[InboxEntry]>;
|
|
15
|
+
}
|
|
16
|
+
declare const IntakeWorkspace: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type IntakeWorkspace = ReturnType<typeof IntakeWorkspace>;
|
|
18
|
+
export default IntakeWorkspace;
|
|
19
|
+
//# sourceMappingURL=IntakeWorkspace.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"IntakeWorkspace.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/IntakeWorkspace.svelte.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAGxE,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,gBAAgB,CAAC;IACvB,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;IAChD,gFAAgF;IAChF,QAAQ,CAAC,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;CAClC;AAmBD,QAAA,MAAM,eAAe,2CAAwC,CAAC;AAC9D,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAC1D,eAAe,eAAe,CAAC"}
|