@ai-matrx/agents 0.18.1 → 0.19.1
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/CHANGELOG.md +73 -0
- package/dist/content-transfer/index.cjs.map +1 -1
- package/dist/content-transfer/index.js.map +1 -1
- package/dist/content-transfer/react/index.cjs.map +1 -1
- package/dist/content-transfer/react/index.js.map +1 -1
- package/dist/mandates/index.cjs +2 -2
- package/dist/mandates/index.cjs.map +1 -1
- package/dist/mandates/index.d.cts +4 -4
- package/dist/mandates/index.d.ts +4 -4
- package/dist/mandates/index.js +2 -2
- package/dist/mandates/index.js.map +1 -1
- package/dist/sources/react/index.cjs +130 -18
- package/dist/sources/react/index.cjs.map +1 -1
- package/dist/sources/react/index.d.cts +46 -5
- package/dist/sources/react/index.d.ts +46 -5
- package/dist/sources/react/index.js +130 -18
- package/dist/sources/react/index.js.map +1 -1
- package/dist/sources/runtime/index.cjs +337 -201
- package/dist/sources/runtime/index.cjs.map +1 -1
- package/dist/sources/runtime/index.d.cts +109 -10
- package/dist/sources/runtime/index.d.ts +109 -10
- package/dist/sources/runtime/index.js +337 -201
- package/dist/sources/runtime/index.js.map +1 -1
- package/mandates/snapshots/keys.0.18.3.json +652 -0
- package/mandates/snapshots/keys.0.18.4.json +652 -0
- package/mandates/snapshots/keys.0.18.5.json +652 -0
- package/mandates/snapshots/keys.0.19.1.json +652 -0
- package/package.json +3 -3
|
@@ -204,6 +204,14 @@ interface SourceDraft {
|
|
|
204
204
|
notes?: string[] | undefined;
|
|
205
205
|
/** The person asked to wait for the clean version before anything runs. */
|
|
206
206
|
waitForClean?: boolean | undefined;
|
|
207
|
+
/**
|
|
208
|
+
* Where the picked parts sit in the Source (label + pages), remembered when
|
|
209
|
+
* they are picked. A Source still being cleaned lists its raw pages; when the
|
|
210
|
+
* clean version lands its parts have NEW ids, and the pick is carried over by
|
|
211
|
+
* page from here (verify-3: "0 characters · 4 of 21 parts"). Persisted with
|
|
212
|
+
* the draft, so it survives a reload.
|
|
213
|
+
*/
|
|
214
|
+
pickedParts?: SourcePickedPart[] | undefined;
|
|
207
215
|
/**
|
|
208
216
|
* What the person handed over, kept ONLY while the Source is still landing
|
|
209
217
|
* (or failed) so a reload or a failure never loses it: the pasted text, the
|
|
@@ -212,6 +220,13 @@ interface SourceDraft {
|
|
|
212
220
|
*/
|
|
213
221
|
input?: SourceIntakeInput | undefined;
|
|
214
222
|
}
|
|
223
|
+
/** One picked part as the person saw it: its id, its label, and the pages it covers. */
|
|
224
|
+
interface SourcePickedPart {
|
|
225
|
+
id: string;
|
|
226
|
+
label: string;
|
|
227
|
+
/** First and last page it covers, when the Source has pages. */
|
|
228
|
+
pages?: [number, number] | undefined;
|
|
229
|
+
}
|
|
215
230
|
type SourceCardStatus = "pending" | "resolving" | "ready" | "error";
|
|
216
231
|
/** One card: the draft, its lifecycle, and what the server measured. */
|
|
217
232
|
interface SourceCardModel {
|
|
@@ -432,8 +447,14 @@ interface SourceSetController extends SourceSetActions {
|
|
|
432
447
|
toSourceSet(options?: {
|
|
433
448
|
targetModelId?: string | undefined;
|
|
434
449
|
}): SourceSet;
|
|
435
|
-
/**
|
|
436
|
-
|
|
450
|
+
/**
|
|
451
|
+
* Apply a set the review returned (forms, parts, caps, delivery). Pass
|
|
452
|
+
* `reviewed` — the set sent INTO the review — and every Source removed there
|
|
453
|
+
* leaves the list too (only those; one that landed meanwhile stays).
|
|
454
|
+
*/
|
|
455
|
+
applySourceSet(set: SourceSet, options?: {
|
|
456
|
+
reviewed?: SourceSet | undefined;
|
|
457
|
+
}): void;
|
|
437
458
|
/** POST /sources/resolve — the grounded text for a generator, named as the cards name them. */
|
|
438
459
|
resolve(options?: {
|
|
439
460
|
targetModelId?: string | undefined;
|
|
@@ -591,15 +612,33 @@ interface SourceIntakeDeps<Body extends SourceLandingBodyBase = SourceLandingBod
|
|
|
591
612
|
interface SourceIntake {
|
|
592
613
|
addPastedText(text: string, name?: string): Promise<void>;
|
|
593
614
|
addWebPage(url: string): Promise<void>;
|
|
594
|
-
/**
|
|
615
|
+
/**
|
|
616
|
+
* A page the host's web picker already read (and the scraper landed); null id
|
|
617
|
+
* = land its text instead. `pendingId` = the card `beginWebPage` made when the
|
|
618
|
+
* read started — it is settled instead of a new card being added.
|
|
619
|
+
*/
|
|
595
620
|
addScrapedPage(page: {
|
|
596
621
|
url: string;
|
|
597
622
|
title: string;
|
|
598
623
|
text: string;
|
|
599
624
|
processedDocumentId: string | null;
|
|
600
|
-
}): Promise<void>;
|
|
625
|
+
}, pendingId?: string | null): Promise<void>;
|
|
626
|
+
/**
|
|
627
|
+
* The host's web picker STARTED reading a link: a card with the link kept
|
|
628
|
+
* appears at once, so a reload mid-read brings it back and lands it (verify-3
|
|
629
|
+
* 12a/12b: a reload while a page was read lost it, link and all). Finish it
|
|
630
|
+
* with `addScrapedPage(page, id)`; if the person drops the preview, remove it;
|
|
631
|
+
* if the picker goes away mid-read, `resume(card)` lands it through the web door.
|
|
632
|
+
*/
|
|
633
|
+
beginWebPage(url: string): string;
|
|
601
634
|
/** Files the host's uploader already uploaded. */
|
|
602
635
|
addUploaded(files: readonly UploadedSourceFile[], kind: SourceKindId): Promise<void>;
|
|
636
|
+
/**
|
|
637
|
+
* A recording made in place (the Voice Pad) that is already written out: it
|
|
638
|
+
* lands as text, but it IS a recording — its card and review say "Recording",
|
|
639
|
+
* never "Pasted text" — and it keeps its name ("Recording 3:41 PM").
|
|
640
|
+
*/
|
|
641
|
+
addRecordedText(text: string, name?: string): Promise<void>;
|
|
603
642
|
/** A recording already uploaded — written out, then landed. */
|
|
604
643
|
addUploadedRecording(file: UploadedSourceFile): Promise<void>;
|
|
605
644
|
/** Something the person already has (Use existing / search). Returns false when already picked. */
|
|
@@ -690,7 +729,9 @@ interface UseSourceSetResult extends SourceSetActions {
|
|
|
690
729
|
targetModelId?: string | undefined;
|
|
691
730
|
}) => SourceSet;
|
|
692
731
|
/** Apply a set the review page returned (forms, parts, caps, delivery). */
|
|
693
|
-
applySourceSet: (set: SourceSet
|
|
732
|
+
applySourceSet: (set: SourceSet, options?: {
|
|
733
|
+
reviewed?: SourceSet | undefined;
|
|
734
|
+
}) => void;
|
|
694
735
|
/** POST /sources/resolve — the grounded text for a generator. */
|
|
695
736
|
resolve: (options?: {
|
|
696
737
|
targetModelId?: string | undefined;
|
|
@@ -204,6 +204,14 @@ interface SourceDraft {
|
|
|
204
204
|
notes?: string[] | undefined;
|
|
205
205
|
/** The person asked to wait for the clean version before anything runs. */
|
|
206
206
|
waitForClean?: boolean | undefined;
|
|
207
|
+
/**
|
|
208
|
+
* Where the picked parts sit in the Source (label + pages), remembered when
|
|
209
|
+
* they are picked. A Source still being cleaned lists its raw pages; when the
|
|
210
|
+
* clean version lands its parts have NEW ids, and the pick is carried over by
|
|
211
|
+
* page from here (verify-3: "0 characters · 4 of 21 parts"). Persisted with
|
|
212
|
+
* the draft, so it survives a reload.
|
|
213
|
+
*/
|
|
214
|
+
pickedParts?: SourcePickedPart[] | undefined;
|
|
207
215
|
/**
|
|
208
216
|
* What the person handed over, kept ONLY while the Source is still landing
|
|
209
217
|
* (or failed) so a reload or a failure never loses it: the pasted text, the
|
|
@@ -212,6 +220,13 @@ interface SourceDraft {
|
|
|
212
220
|
*/
|
|
213
221
|
input?: SourceIntakeInput | undefined;
|
|
214
222
|
}
|
|
223
|
+
/** One picked part as the person saw it: its id, its label, and the pages it covers. */
|
|
224
|
+
interface SourcePickedPart {
|
|
225
|
+
id: string;
|
|
226
|
+
label: string;
|
|
227
|
+
/** First and last page it covers, when the Source has pages. */
|
|
228
|
+
pages?: [number, number] | undefined;
|
|
229
|
+
}
|
|
215
230
|
type SourceCardStatus = "pending" | "resolving" | "ready" | "error";
|
|
216
231
|
/** One card: the draft, its lifecycle, and what the server measured. */
|
|
217
232
|
interface SourceCardModel {
|
|
@@ -432,8 +447,14 @@ interface SourceSetController extends SourceSetActions {
|
|
|
432
447
|
toSourceSet(options?: {
|
|
433
448
|
targetModelId?: string | undefined;
|
|
434
449
|
}): SourceSet;
|
|
435
|
-
/**
|
|
436
|
-
|
|
450
|
+
/**
|
|
451
|
+
* Apply a set the review returned (forms, parts, caps, delivery). Pass
|
|
452
|
+
* `reviewed` — the set sent INTO the review — and every Source removed there
|
|
453
|
+
* leaves the list too (only those; one that landed meanwhile stays).
|
|
454
|
+
*/
|
|
455
|
+
applySourceSet(set: SourceSet, options?: {
|
|
456
|
+
reviewed?: SourceSet | undefined;
|
|
457
|
+
}): void;
|
|
437
458
|
/** POST /sources/resolve — the grounded text for a generator, named as the cards name them. */
|
|
438
459
|
resolve(options?: {
|
|
439
460
|
targetModelId?: string | undefined;
|
|
@@ -591,15 +612,33 @@ interface SourceIntakeDeps<Body extends SourceLandingBodyBase = SourceLandingBod
|
|
|
591
612
|
interface SourceIntake {
|
|
592
613
|
addPastedText(text: string, name?: string): Promise<void>;
|
|
593
614
|
addWebPage(url: string): Promise<void>;
|
|
594
|
-
/**
|
|
615
|
+
/**
|
|
616
|
+
* A page the host's web picker already read (and the scraper landed); null id
|
|
617
|
+
* = land its text instead. `pendingId` = the card `beginWebPage` made when the
|
|
618
|
+
* read started — it is settled instead of a new card being added.
|
|
619
|
+
*/
|
|
595
620
|
addScrapedPage(page: {
|
|
596
621
|
url: string;
|
|
597
622
|
title: string;
|
|
598
623
|
text: string;
|
|
599
624
|
processedDocumentId: string | null;
|
|
600
|
-
}): Promise<void>;
|
|
625
|
+
}, pendingId?: string | null): Promise<void>;
|
|
626
|
+
/**
|
|
627
|
+
* The host's web picker STARTED reading a link: a card with the link kept
|
|
628
|
+
* appears at once, so a reload mid-read brings it back and lands it (verify-3
|
|
629
|
+
* 12a/12b: a reload while a page was read lost it, link and all). Finish it
|
|
630
|
+
* with `addScrapedPage(page, id)`; if the person drops the preview, remove it;
|
|
631
|
+
* if the picker goes away mid-read, `resume(card)` lands it through the web door.
|
|
632
|
+
*/
|
|
633
|
+
beginWebPage(url: string): string;
|
|
601
634
|
/** Files the host's uploader already uploaded. */
|
|
602
635
|
addUploaded(files: readonly UploadedSourceFile[], kind: SourceKindId): Promise<void>;
|
|
636
|
+
/**
|
|
637
|
+
* A recording made in place (the Voice Pad) that is already written out: it
|
|
638
|
+
* lands as text, but it IS a recording — its card and review say "Recording",
|
|
639
|
+
* never "Pasted text" — and it keeps its name ("Recording 3:41 PM").
|
|
640
|
+
*/
|
|
641
|
+
addRecordedText(text: string, name?: string): Promise<void>;
|
|
603
642
|
/** A recording already uploaded — written out, then landed. */
|
|
604
643
|
addUploadedRecording(file: UploadedSourceFile): Promise<void>;
|
|
605
644
|
/** Something the person already has (Use existing / search). Returns false when already picked. */
|
|
@@ -690,7 +729,9 @@ interface UseSourceSetResult extends SourceSetActions {
|
|
|
690
729
|
targetModelId?: string | undefined;
|
|
691
730
|
}) => SourceSet;
|
|
692
731
|
/** Apply a set the review page returned (forms, parts, caps, delivery). */
|
|
693
|
-
applySourceSet: (set: SourceSet
|
|
732
|
+
applySourceSet: (set: SourceSet, options?: {
|
|
733
|
+
reviewed?: SourceSet | undefined;
|
|
734
|
+
}) => void;
|
|
694
735
|
/** POST /sources/resolve — the grounded text for a generator. */
|
|
695
736
|
resolve: (options?: {
|
|
696
737
|
targetModelId?: string | undefined;
|
|
@@ -100,12 +100,12 @@ function deliveryPatch(delivery) {
|
|
|
100
100
|
var DELIVERY_WORDS = {
|
|
101
101
|
direct: {
|
|
102
102
|
label: "Include the text",
|
|
103
|
-
hint: "
|
|
103
|
+
hint: "Sent in full",
|
|
104
104
|
summary: "Text included"
|
|
105
105
|
},
|
|
106
106
|
context: {
|
|
107
107
|
label: "Let the AI look it up",
|
|
108
|
-
hint: "
|
|
108
|
+
hint: "Read as needed",
|
|
109
109
|
summary: "Looked up when needed"
|
|
110
110
|
}
|
|
111
111
|
};
|
|
@@ -128,6 +128,13 @@ function fitDelivery(ref, deliveries) {
|
|
|
128
128
|
|
|
129
129
|
// sources/runtime/partsSearch.ts
|
|
130
130
|
var RANGE_RE = /^(\d+)\s*(?:-|–|—|to)\s*(\d+)$/;
|
|
131
|
+
var SPAN_RE = /(\d+)\s*[–—-]\s*(\d+)/;
|
|
132
|
+
function partPages(part) {
|
|
133
|
+
if (part.page === void 0 || part.page === null) return null;
|
|
134
|
+
const span = /^pages?\s/i.test(part.label) ? SPAN_RE.exec(part.label) : null;
|
|
135
|
+
if (span) return [Number(span[1]), Number(span[2])];
|
|
136
|
+
return [part.page, part.page];
|
|
137
|
+
}
|
|
131
138
|
function isWordQuery(query) {
|
|
132
139
|
const q = query.trim().toLowerCase();
|
|
133
140
|
return q.length > 0 && !/^\d+$/.test(q) && !RANGE_RE.test(q);
|
|
@@ -146,7 +153,7 @@ function resumableInput(draft) {
|
|
|
146
153
|
case "youtube":
|
|
147
154
|
return input.url?.trim() ? input : null;
|
|
148
155
|
case "audio":
|
|
149
|
-
return input.fileId ? input : null;
|
|
156
|
+
return input.fileId || input.text?.trim() ? input : null;
|
|
150
157
|
default:
|
|
151
158
|
return null;
|
|
152
159
|
}
|
|
@@ -161,12 +168,12 @@ function reloadedCard(draft, status) {
|
|
|
161
168
|
return { action: "reoffer", sentence: uploadCutOffSentence(draft) };
|
|
162
169
|
return { action: "lost", sentence: RELOADED_WHILE_ADDING };
|
|
163
170
|
}
|
|
164
|
-
var NOT_KEPT_FOR_RELOAD = "Too large to
|
|
171
|
+
var NOT_KEPT_FOR_RELOAD = "Too large to restore if the page reloads before it is added.";
|
|
165
172
|
var WAITING_FOR_ORGANIZATION = "Waiting for an organization \u2014 choose one and this continues by itself.";
|
|
166
173
|
var KEEP_WAITING_FOR_ORGANIZATION = "Read. Waiting for an organization to keep it \u2014 choose one and it is kept by itself.";
|
|
167
174
|
|
|
168
175
|
// sources/runtime/fileSource.ts
|
|
169
|
-
var UNREADABLE = "
|
|
176
|
+
var UNREADABLE = "Can't read this file \u2014 add a PDF, Word or text copy.";
|
|
170
177
|
function nextFileStep(status, ctx) {
|
|
171
178
|
const pdId = status?.processed_document_id ?? null;
|
|
172
179
|
if (pdId) return { do: "keep", processedDocumentId: pdId };
|
|
@@ -174,7 +181,7 @@ function nextFileStep(status, ctx) {
|
|
|
174
181
|
if (status?.state === "failed")
|
|
175
182
|
return {
|
|
176
183
|
do: "unreadable",
|
|
177
|
-
reason: status.error?.message ? `
|
|
184
|
+
reason: status.error?.message ? `Can't read this file: ${status.error.message}. Add it again.` : UNREADABLE
|
|
178
185
|
};
|
|
179
186
|
if (!ctx.started) return { do: "start" };
|
|
180
187
|
return { do: "unreadable", reason: UNREADABLE };
|
|
@@ -195,6 +202,8 @@ function fileCardHeldForOrganization(card, activeOrganizationId, fileOrganizatio
|
|
|
195
202
|
|
|
196
203
|
// sources/runtime/state.ts
|
|
197
204
|
var EMPTY_SOURCE_SET_STATE = Object.freeze({ cards: Object.freeze([]), topic: "" });
|
|
205
|
+
var PARTS_MATCHED_BY_PAGE = "The clean version is ready \u2014 your parts were matched to it by page.";
|
|
206
|
+
var PARTS_NOT_MATCHED = "The clean version is ready, but your parts could not be matched to it \u2014 choose parts again. Until then the whole Source goes in.";
|
|
198
207
|
var SAME_SOURCE_AGAIN = "You added this again \u2014 it is the same Source, so it is listed once.";
|
|
199
208
|
function isSourceDraft(value) {
|
|
200
209
|
return !!value && typeof value === "object" && typeof value.kind === "string" && typeof value.label === "string";
|
|
@@ -261,7 +270,7 @@ function settleCard(state, id, patch, options = {}) {
|
|
|
261
270
|
}));
|
|
262
271
|
}
|
|
263
272
|
function failCard(state, id, sentence) {
|
|
264
|
-
return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence }));
|
|
273
|
+
return replaceCard(state, id, (c) => ({ ...c, status: "error", error: sentence, manifest: null }));
|
|
265
274
|
}
|
|
266
275
|
function restartCard(state, id) {
|
|
267
276
|
return replaceCard(state, id, (c) => ({ ...c, status: "resolving", error: null }));
|
|
@@ -277,7 +286,68 @@ function updateCardRef(state, id, options) {
|
|
|
277
286
|
return replaceCard(state, id, (c) => {
|
|
278
287
|
if (!c.draft.ref) return c;
|
|
279
288
|
const ref = createSourceRef(c.draft.ref.resource_type, c.draft.ref.resource_id, { ...c.draft.ref, ...options });
|
|
280
|
-
return { ...c, draft: { ...c.draft, ref } };
|
|
289
|
+
return rememberPickedParts({ ...c, draft: { ...c.draft, ref } });
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
function rememberPickedParts(card) {
|
|
293
|
+
const ids = card.draft.ref?.include_segments ?? [];
|
|
294
|
+
if (ids.length === 0) {
|
|
295
|
+
return card.draft.pickedParts === void 0 ? card : { ...card, draft: { ...card.draft, pickedParts: void 0 } };
|
|
296
|
+
}
|
|
297
|
+
const segments = card.manifest?.segments ?? [];
|
|
298
|
+
const known = new Map((card.draft.pickedParts ?? []).map((p) => [p.id, p]));
|
|
299
|
+
const picked = [];
|
|
300
|
+
for (const id of ids) {
|
|
301
|
+
const segment = segments.find((s) => s.id === id);
|
|
302
|
+
if (segment) {
|
|
303
|
+
const pages = partPages(segment);
|
|
304
|
+
picked.push({ id, label: segment.label, ...pages ? { pages } : {} });
|
|
305
|
+
} else {
|
|
306
|
+
const before = known.get(id);
|
|
307
|
+
if (before) picked.push(before);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return { ...card, draft: { ...card.draft, pickedParts: picked.length ? picked : void 0 } };
|
|
311
|
+
}
|
|
312
|
+
function withNote(notes, note, drop = []) {
|
|
313
|
+
const kept = (notes ?? []).filter((n) => n !== note && !drop.includes(n));
|
|
314
|
+
return [...kept, note];
|
|
315
|
+
}
|
|
316
|
+
function carryPickedParts(card, entry) {
|
|
317
|
+
const ref = card.draft.ref;
|
|
318
|
+
const picked = ref?.include_segments ?? [];
|
|
319
|
+
const segments = entry.segments ?? [];
|
|
320
|
+
if (!ref || picked.length === 0 || segments.length === 0) return card;
|
|
321
|
+
const ids = new Set(segments.map((s) => s.id));
|
|
322
|
+
const stale = picked.filter((id) => !ids.has(id));
|
|
323
|
+
if (stale.length === 0) return card;
|
|
324
|
+
const remembered = new Map((card.draft.pickedParts ?? []).map((p) => [p.id, p]));
|
|
325
|
+
const oldSegments = card.manifest?.segments ?? [];
|
|
326
|
+
const spans = [];
|
|
327
|
+
let unknown = 0;
|
|
328
|
+
for (const id of stale) {
|
|
329
|
+
const old = oldSegments.find((s) => s.id === id);
|
|
330
|
+
const pages = remembered.get(id)?.pages ?? (old ? partPages(old) : null);
|
|
331
|
+
if (pages) spans.push(pages);
|
|
332
|
+
else unknown += 1;
|
|
333
|
+
}
|
|
334
|
+
const keep = new Set(picked.filter((id) => ids.has(id)));
|
|
335
|
+
for (const segment of segments) {
|
|
336
|
+
const pages = partPages(segment);
|
|
337
|
+
if (pages && spans.some(([from, to]) => pages[0] <= to && pages[1] >= from)) keep.add(segment.id);
|
|
338
|
+
}
|
|
339
|
+
const nextIds = segments.filter((s) => keep.has(s.id)).map((s) => s.id);
|
|
340
|
+
const matched = unknown === 0 && nextIds.length > 0;
|
|
341
|
+
const nextRef = createSourceRef(ref.resource_type, ref.resource_id, {
|
|
342
|
+
...ref,
|
|
343
|
+
include_segments: matched ? nextIds : void 0
|
|
344
|
+
});
|
|
345
|
+
const note = matched ? PARTS_MATCHED_BY_PAGE : PARTS_NOT_MATCHED;
|
|
346
|
+
const other = matched ? PARTS_NOT_MATCHED : PARTS_MATCHED_BY_PAGE;
|
|
347
|
+
return rememberPickedParts({
|
|
348
|
+
...card,
|
|
349
|
+
manifest: entry,
|
|
350
|
+
draft: { ...card.draft, ref: nextRef, notes: withNote(card.draft.notes, note, [other]) }
|
|
281
351
|
});
|
|
282
352
|
}
|
|
283
353
|
function setCardWaitForClean(state, id, wait) {
|
|
@@ -286,14 +356,24 @@ function setCardWaitForClean(state, id, wait) {
|
|
|
286
356
|
function setSourceTopic(state, topic) {
|
|
287
357
|
return state.topic === topic ? state : { ...state, topic };
|
|
288
358
|
}
|
|
289
|
-
function
|
|
359
|
+
function removedInReview(reviewed, answer) {
|
|
360
|
+
const kept = new Set(answer.sources.map(sourceKey));
|
|
361
|
+
return new Set(reviewed.sources.map(sourceKey).filter((k) => !kept.has(k)));
|
|
362
|
+
}
|
|
363
|
+
function applyReviewedSourceSet(state, set, reviewed) {
|
|
290
364
|
let next = state;
|
|
291
|
-
|
|
365
|
+
if (reviewed) {
|
|
366
|
+
const removed = removedInReview(reviewed, set);
|
|
367
|
+
for (const card of state.cards) {
|
|
368
|
+
if (card.draft.ref && removed.has(sourceKey(card.draft.ref))) next = removeCard(next, card.id);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
for (const card of next.cards) {
|
|
292
372
|
const ref = card.draft.ref;
|
|
293
373
|
if (!ref) continue;
|
|
294
374
|
const match = set.sources.find((r) => sameSourceRef(r, ref));
|
|
295
375
|
if (!match) continue;
|
|
296
|
-
next = replaceCard(next, card.id, (c) => ({ ...c, draft: { ...c.draft, ref: match } }));
|
|
376
|
+
next = replaceCard(next, card.id, (c) => rememberPickedParts({ ...c, draft: { ...c.draft, ref: match } }));
|
|
297
377
|
}
|
|
298
378
|
return set.topic !== void 0 ? setSourceTopic(next, set.topic) : next;
|
|
299
379
|
}
|
|
@@ -303,7 +383,13 @@ function applyManifest(state, manifest) {
|
|
|
303
383
|
const ref = card.draft.ref;
|
|
304
384
|
if (!ref || card.status !== "ready") continue;
|
|
305
385
|
const entry = manifest.sources.find((e) => sameSourceRef(e.ref, ref));
|
|
306
|
-
if (entry)
|
|
386
|
+
if (!entry) continue;
|
|
387
|
+
next = replaceCard(next, card.id, (c) => {
|
|
388
|
+
const carried = carryPickedParts(c, entry);
|
|
389
|
+
if (carried !== c) return carried;
|
|
390
|
+
const measured = { ...c, manifest: entry };
|
|
391
|
+
return c.draft.ref?.include_segments?.length && !c.draft.pickedParts ? rememberPickedParts(measured) : measured;
|
|
392
|
+
});
|
|
307
393
|
}
|
|
308
394
|
return next;
|
|
309
395
|
}
|
|
@@ -498,8 +584,8 @@ function createSourceSetController(adapter) {
|
|
|
498
584
|
hasRef: (resourceType, resourceId) => hasSourceRef(store.getState(), resourceType, resourceId),
|
|
499
585
|
liveCount: () => store.getState().cards.length,
|
|
500
586
|
toSourceSet: (options = {}) => selectSourceSet(store.getState(), options),
|
|
501
|
-
applySourceSet(set) {
|
|
502
|
-
commit(applyReviewedSourceSet(store.getState(), set));
|
|
587
|
+
applySourceSet(set, options = {}) {
|
|
588
|
+
commit(applyReviewedSourceSet(store.getState(), set, options.reviewed));
|
|
503
589
|
},
|
|
504
590
|
async manifest() {
|
|
505
591
|
const refs = readySourceRefs(store.getState());
|
|
@@ -784,20 +870,45 @@ function createSourceIntake(set, deps) {
|
|
|
784
870
|
});
|
|
785
871
|
await hold(() => landPaste(id, trimmed, name));
|
|
786
872
|
},
|
|
873
|
+
async addRecordedText(text, name) {
|
|
874
|
+
const trimmed = text.trim();
|
|
875
|
+
if (!trimmed) return;
|
|
876
|
+
const max = deps.maxKeptChars();
|
|
877
|
+
const kept = max !== null && trimmed.length <= max;
|
|
878
|
+
const id = set.addPending({
|
|
879
|
+
kind: "audio",
|
|
880
|
+
label: name?.trim() || "Recording",
|
|
881
|
+
origin: "Recording",
|
|
882
|
+
input: kept ? { text: trimmed, name } : void 0,
|
|
883
|
+
notes: kept ? void 0 : [NOT_KEPT_FOR_RELOAD]
|
|
884
|
+
});
|
|
885
|
+
await hold(() => landPaste(id, trimmed, name));
|
|
886
|
+
},
|
|
787
887
|
async addWebPage(raw) {
|
|
788
888
|
const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
|
|
789
889
|
const id = set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
|
|
790
890
|
await hold(() => landWebPage(id, url));
|
|
791
891
|
},
|
|
792
|
-
|
|
892
|
+
beginWebPage(raw) {
|
|
893
|
+
const url = /^https?:\/\//i.test(raw.trim()) ? raw.trim() : `https://${raw.trim()}`;
|
|
894
|
+
return set.addPending({ kind: "web", label: hostOf(url), origin: url, input: { url } });
|
|
895
|
+
},
|
|
896
|
+
async addScrapedPage(page, pendingId) {
|
|
793
897
|
const label = page.title || hostOf(page.url);
|
|
898
|
+
const cardFor = () => {
|
|
899
|
+
if (pendingId && set.restart(pendingId)) {
|
|
900
|
+
set.updateDraft(pendingId, { label, origin: page.url, input: { url: page.url } });
|
|
901
|
+
return pendingId;
|
|
902
|
+
}
|
|
903
|
+
return set.addPending({ kind: "web", label, origin: page.url, input: { url: page.url } });
|
|
904
|
+
};
|
|
794
905
|
if (!page.processedDocumentId) {
|
|
795
|
-
const id2 =
|
|
906
|
+
const id2 = cardFor();
|
|
796
907
|
await hold(() => landPaste(id2, page.text, label));
|
|
797
908
|
return;
|
|
798
909
|
}
|
|
799
910
|
const pdId = page.processedDocumentId;
|
|
800
|
-
const id =
|
|
911
|
+
const id = cardFor();
|
|
801
912
|
await hold(async () => {
|
|
802
913
|
let notes = [];
|
|
803
914
|
try {
|
|
@@ -872,7 +983,8 @@ function createSourceIntake(set, deps) {
|
|
|
872
983
|
return true;
|
|
873
984
|
}
|
|
874
985
|
case "audio":
|
|
875
|
-
void transcribeRecording(card.id, input.fileId
|
|
986
|
+
if (input.fileId) void transcribeRecording(card.id, input.fileId, card.draft.label);
|
|
987
|
+
else void landPaste(card.id, (input.text ?? "").trim(), input.name);
|
|
876
988
|
return true;
|
|
877
989
|
default:
|
|
878
990
|
return false;
|