@arbidocs/react 0.4.9 → 0.4.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/artifacts/index.cjs +168 -28
- package/dist/artifacts/index.cjs.map +1 -1
- package/dist/artifacts/index.d.cts +121 -9
- package/dist/artifacts/index.d.ts +121 -9
- package/dist/artifacts/index.js +134 -23
- package/dist/artifacts/index.js.map +1 -1
- package/dist/artifacts/redline-core.cjs +8 -4
- package/dist/artifacts/redline-core.d.cts +7 -4
- package/dist/artifacts/redline-core.d.ts +7 -4
- package/dist/artifacts/redline-core.js +1 -1
- package/dist/{chunk-RKWNX32A.js → chunk-43AKLNBC.js} +55 -10
- package/dist/chunk-43AKLNBC.js.map +1 -0
- package/dist/{chunk-VJWAP2HJ.cjs → chunk-PATLJSAO.cjs} +61 -9
- package/dist/chunk-PATLJSAO.cjs.map +1 -0
- package/dist/{chunk-R5SR7R6W.js → chunk-QFELRTTG.js} +105 -3
- package/dist/chunk-QFELRTTG.js.map +1 -0
- package/dist/{chunk-E7UO2JSR.cjs → chunk-VZLGOA4A.cjs} +110 -2
- package/dist/chunk-VZLGOA4A.cjs.map +1 -0
- package/dist/index.cjs +141 -117
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -4
- package/dist/stored-markdown-DMwFzWhc.d.cts +75 -0
- package/dist/stored-markdown-DMwFzWhc.d.ts +75 -0
- package/package.json +6 -3
- package/dist/chunk-E7UO2JSR.cjs.map +0 -1
- package/dist/chunk-R5SR7R6W.js.map +0 -1
- package/dist/chunk-RKWNX32A.js.map +0 -1
- package/dist/chunk-VJWAP2HJ.cjs.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { RedlineWithRegions, RedlineRegion } from './redline-core.cjs';
|
|
2
|
-
export { RegionKind, buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, revertRedlineRegion } from './redline-core.cjs';
|
|
2
|
+
export { RegionKind, buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, normalizeMarkdown, revertRedlineRegion } from './redline-core.cjs';
|
|
3
3
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
4
|
+
export { c as cleanDraftMarkdown, p as promoteBoldTitle, r as repairParsedTitle, s as stripAgentPreamble, u as unescapeMarkdown, a as unwrapCitationLinks } from '../stored-markdown-DMwFzWhc.cjs';
|
|
4
5
|
import { FC, ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
6
|
+
import { ArtifactEvent } from '@arbidocs/sdk/browser';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Where a redline is computed. The default runs on the calling thread; a host with large
|
|
@@ -87,10 +89,19 @@ declare function toArtifactVersions(chain: readonly VersionedDoc[]): ArtifactVer
|
|
|
87
89
|
* (the lineage is just this document, or it is unknown), so unversioned documents stay unlabelled.
|
|
88
90
|
*/
|
|
89
91
|
declare function versionLabel(versions: readonly ArtifactVersion[], docId: string | null | undefined): string | null;
|
|
90
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Join parsed chunks into one markdown text (blank line between chunks).
|
|
94
|
+
*
|
|
95
|
+
* `unescape: true` for an artifact stored from markdown: the backend's parse of a `.md` escapes
|
|
96
|
+
* markdown punctuation, which `unescapeMarkdown` undoes (see its rule), and mangles its title
|
|
97
|
+
* (`repairParsedTitle`). Leave it off for the parse
|
|
98
|
+
* of a PDF or Word file.
|
|
99
|
+
*/
|
|
91
100
|
declare function flattenChunks(chunks: readonly {
|
|
92
101
|
content?: string | null;
|
|
93
|
-
}[] | null | undefined
|
|
102
|
+
}[] | null | undefined, options?: {
|
|
103
|
+
unescape?: boolean;
|
|
104
|
+
}): string;
|
|
94
105
|
/** Is `id` a real, saved document id? Local drafts have placeholder ids and no backend document. */
|
|
95
106
|
declare function isDocumentId(id: string | null | undefined): id is string;
|
|
96
107
|
|
|
@@ -173,13 +184,56 @@ interface ArtifactEditSession {
|
|
|
173
184
|
*/
|
|
174
185
|
declare function useArtifactEditSession(options: ArtifactEditSessionOptions): ArtifactEditSession;
|
|
175
186
|
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Where the text of a stored artifact is:
|
|
189
|
+
* - `loading`: the first request is in flight (or there is no document yet);
|
|
190
|
+
* - `parsing`: the backend has not finished parsing the upload (the endpoint answers 404, 422 or
|
|
191
|
+
* 500, or an empty parse, for a few seconds after an upload) and the hook is retrying;
|
|
192
|
+
* - `ready`: the text is here (an empty document is `ready` with `text: null`);
|
|
193
|
+
* - `error`: the retries ran out, or the request was refused (401/403).
|
|
194
|
+
*/
|
|
195
|
+
type ArtifactTextStatus = 'loading' | 'parsing' | 'ready' | 'error';
|
|
196
|
+
/** How long `useArtifactText` keeps asking while a document is still being parsed. */
|
|
197
|
+
interface ArtifactTextRetry {
|
|
198
|
+
/** Retries after the first request. Default 12. */
|
|
199
|
+
attempts?: number;
|
|
200
|
+
/** Delay before the first retry; it grows by 1.6x each time. Default 1000 ms. */
|
|
201
|
+
baseDelayMs?: number;
|
|
202
|
+
/** Cap for one delay. Default 5000 ms (12 retries are about 50 s in all). */
|
|
203
|
+
maxDelayMs?: number;
|
|
204
|
+
}
|
|
205
|
+
declare const DEFAULT_ARTIFACT_TEXT_RETRY: Required<ArtifactTextRetry>;
|
|
206
|
+
interface ArtifactText {
|
|
207
|
+
/**
|
|
208
|
+
* The markdown, clean: `undefined` while `loading` or `parsing`, `null` when there is none (an
|
|
209
|
+
* empty document, or `error`).
|
|
210
|
+
*/
|
|
180
211
|
text: string | null | undefined;
|
|
212
|
+
/** `status` is `loading` or `parsing`. */
|
|
181
213
|
isLoading: boolean;
|
|
182
|
-
|
|
214
|
+
status: ArtifactTextStatus;
|
|
215
|
+
/** The last request error while `parsing` or after `error`. */
|
|
216
|
+
error: Error | null;
|
|
217
|
+
/** Ask again now (after an `error`). */
|
|
218
|
+
retry: () => void;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* The saved markdown of an artifact version (its parsed chunks, joined and unescaped).
|
|
222
|
+
*
|
|
223
|
+
* A freshly uploaded document is parsed asynchronously, and for roughly 3 to 20 seconds the parsed
|
|
224
|
+
* content endpoint answers 404, 422 or 500 (or an empty parse). The hook therefore retries with a
|
|
225
|
+
* growing delay (see `ArtifactTextRetry`) and reports `status: 'parsing'` meanwhile, so a host can
|
|
226
|
+
* show "Preparing editor..." instead of opening an editor on an empty baseline. After the last
|
|
227
|
+
* retry the status is `error`; `retry()` starts a new round.
|
|
228
|
+
*
|
|
229
|
+
* The text is run through `unescapeMarkdown`: an artifact is always stored from markdown, and the
|
|
230
|
+
* backend's parse of a `.md` escapes its punctuation.
|
|
231
|
+
*/
|
|
232
|
+
declare function useArtifactText(docId: string | null | undefined, options?: {
|
|
233
|
+
enabled?: boolean;
|
|
234
|
+
retry?: ArtifactTextRetry;
|
|
235
|
+
}): ArtifactText;
|
|
236
|
+
|
|
183
237
|
/** The version lineage of an artifact in a workspace, oldest first. */
|
|
184
238
|
declare function useArtifactVersions(workspaceId: string | undefined, docId: string | null | undefined): ArtifactVersion[];
|
|
185
239
|
interface SaveArtifactVersionInput {
|
|
@@ -236,6 +290,13 @@ interface ArtifactSessionView {
|
|
|
236
290
|
onCompare: () => void;
|
|
237
291
|
onStopCompare: () => void;
|
|
238
292
|
onToggleTrackChanges: () => void;
|
|
293
|
+
/**
|
|
294
|
+
* Whether the text is ready. `ArtifactEditor` renders "Preparing editor..." while it is
|
|
295
|
+
* `loading` or `parsing` (a fresh upload is parsed for a few seconds) and an error with Retry.
|
|
296
|
+
*/
|
|
297
|
+
status: ArtifactTextStatus;
|
|
298
|
+
/** Ask for the text again after an `error`. */
|
|
299
|
+
onRetry: () => void;
|
|
239
300
|
}
|
|
240
301
|
interface ArtifactSession extends ArtifactEditSession {
|
|
241
302
|
/** The open document: the new real id once a local draft has been saved. */
|
|
@@ -243,6 +304,8 @@ interface ArtifactSession extends ArtifactEditSession {
|
|
|
243
304
|
/** The open version is an unsaved local draft (no backend document yet). */
|
|
244
305
|
isLocal: boolean;
|
|
245
306
|
isLoading: boolean;
|
|
307
|
+
/** See `ArtifactTextStatus`. */
|
|
308
|
+
status: ArtifactTextStatus;
|
|
246
309
|
versions: ArtifactVersion[];
|
|
247
310
|
/** "v2" for any version in the lineage. */
|
|
248
311
|
labelOf: (id: string | null | undefined) => string | null;
|
|
@@ -289,4 +352,53 @@ interface ArtifactRedlineComponents {
|
|
|
289
352
|
*/
|
|
290
353
|
declare function renderArtifactMarkdown(markdown: string, components?: ArtifactRedlineComponents): ReactNode;
|
|
291
354
|
|
|
292
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Where a drafted artifact is in its STREAM -> PERSIST -> EDIT life:
|
|
357
|
+
* - `streaming`: the agent is still working (or has produced nothing final): show the live canvas;
|
|
358
|
+
* - `persisting`: the finished draft is being stored as version 1 of the artifact;
|
|
359
|
+
* - `parsing`: stored; the backend is still parsing it, so there is no editable text yet;
|
|
360
|
+
* - `ready`: the text is readable: mount the editor on `docId`;
|
|
361
|
+
* - `error`: storing failed or the parse never produced text (`retry()` starts over).
|
|
362
|
+
*/
|
|
363
|
+
type StreamedArtifactPhase = 'streaming' | 'persisting' | 'parsing' | 'ready' | 'error';
|
|
364
|
+
interface UseStreamedArtifactOptions {
|
|
365
|
+
/** The artifacts of the agent run (`useConnectedAiTask().artifacts`). */
|
|
366
|
+
artifacts: ArtifactEvent[];
|
|
367
|
+
workspaceId: string | undefined;
|
|
368
|
+
/**
|
|
369
|
+
* The run has stopped: not busy and not failed. Nothing is stored before this, so a draft that
|
|
370
|
+
* is still streaming is never persisted half-written.
|
|
371
|
+
*/
|
|
372
|
+
settled: boolean;
|
|
373
|
+
/** Make the stored markdown from the streamed content. Defaults to `cleanDraftMarkdown`. */
|
|
374
|
+
clean?: (streamed: string) => string;
|
|
375
|
+
/** Processing config for the stored version. */
|
|
376
|
+
configId?: string | null;
|
|
377
|
+
/** How long to wait for the parse (see `useArtifactText`). */
|
|
378
|
+
parseRetry?: ArtifactTextRetry;
|
|
379
|
+
}
|
|
380
|
+
interface StreamedArtifact {
|
|
381
|
+
phase: StreamedArtifactPhase;
|
|
382
|
+
/** The finished streamed artifact (its `title` names the stored versions). */
|
|
383
|
+
artifact: ArtifactEvent | undefined;
|
|
384
|
+
/** The backend document to edit, once there is one (while `parsing` and `ready`). */
|
|
385
|
+
docId: string | undefined;
|
|
386
|
+
error: Error | null;
|
|
387
|
+
/** After an `error`: store again or ask for the parse again. */
|
|
388
|
+
retry: () => void;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The lifecycle of an agent-drafted artifact, so a host passes the stream and renders the phase.
|
|
392
|
+
*
|
|
393
|
+
* While the agent streams, the draft lives only on the canvas. Once the run is `settled` and the
|
|
394
|
+
* draft is complete, a draft that is not yet a backend document is cleaned (`cleanDraftMarkdown`)
|
|
395
|
+
* and stored ONCE as the artifact's first version (`wpType: 'artifact'`); a draft that already is a
|
|
396
|
+
* backend document passes through. The hook then waits for the parse (`useArtifactText`, which
|
|
397
|
+
* retries) and reports `ready` with the `docId` to open in an editor.
|
|
398
|
+
*
|
|
399
|
+
* Needs an `<ArbiProvider>` and a workspace. `@arbidocs/blocks/connect` has the whole thing as a
|
|
400
|
+
* component (`StreamedArtifact`).
|
|
401
|
+
*/
|
|
402
|
+
declare function useStreamedArtifact({ artifacts, workspaceId, settled, clean, configId, parseRetry, }: UseStreamedArtifactOptions): StreamedArtifact;
|
|
403
|
+
|
|
404
|
+
export { type ArtifactEditSession, type ArtifactEditSessionOptions, type ArtifactRedlineComponents, type ArtifactSaveResult, type ArtifactSession, type ArtifactSessionOptions, type ArtifactSessionView, type ArtifactText, type ArtifactTextRetry, type ArtifactTextStatus, type ArtifactVersion, DEFAULT_ARTIFACT_TEXT_RETRY, type RedlineEngine, RedlineRegion, type RedlineState, RedlineWithRegions, type SaveArtifactVersionInput, type StreamedArtifact, type StreamedArtifactPhase, type UseRedlineOptions, type UseRedlineResult, type UseStreamedArtifactOptions, type VersionedDoc, buildVersionChain, flattenChunks, isDocumentId, mainThreadRedlineEngine, renderArtifactMarkdown, toArtifactVersions, useArtifactEditSession, useArtifactSession, useArtifactText, useArtifactVersions, useRedline, useSaveArtifactVersion, useStreamedArtifact, versionLabel };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { RedlineWithRegions, RedlineRegion } from './redline-core.js';
|
|
2
|
-
export { RegionKind, buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, revertRedlineRegion } from './redline-core.js';
|
|
2
|
+
export { RegionKind, buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, normalizeMarkdown, revertRedlineRegion } from './redline-core.js';
|
|
3
3
|
import * as _tanstack_react_query from '@tanstack/react-query';
|
|
4
|
+
export { c as cleanDraftMarkdown, p as promoteBoldTitle, r as repairParsedTitle, s as stripAgentPreamble, u as unescapeMarkdown, a as unwrapCitationLinks } from '../stored-markdown-DMwFzWhc.js';
|
|
4
5
|
import { FC, ComponentPropsWithoutRef, ReactNode } from 'react';
|
|
6
|
+
import { ArtifactEvent } from '@arbidocs/sdk/browser';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* Where a redline is computed. The default runs on the calling thread; a host with large
|
|
@@ -87,10 +89,19 @@ declare function toArtifactVersions(chain: readonly VersionedDoc[]): ArtifactVer
|
|
|
87
89
|
* (the lineage is just this document, or it is unknown), so unversioned documents stay unlabelled.
|
|
88
90
|
*/
|
|
89
91
|
declare function versionLabel(versions: readonly ArtifactVersion[], docId: string | null | undefined): string | null;
|
|
90
|
-
/**
|
|
92
|
+
/**
|
|
93
|
+
* Join parsed chunks into one markdown text (blank line between chunks).
|
|
94
|
+
*
|
|
95
|
+
* `unescape: true` for an artifact stored from markdown: the backend's parse of a `.md` escapes
|
|
96
|
+
* markdown punctuation, which `unescapeMarkdown` undoes (see its rule), and mangles its title
|
|
97
|
+
* (`repairParsedTitle`). Leave it off for the parse
|
|
98
|
+
* of a PDF or Word file.
|
|
99
|
+
*/
|
|
91
100
|
declare function flattenChunks(chunks: readonly {
|
|
92
101
|
content?: string | null;
|
|
93
|
-
}[] | null | undefined
|
|
102
|
+
}[] | null | undefined, options?: {
|
|
103
|
+
unescape?: boolean;
|
|
104
|
+
}): string;
|
|
94
105
|
/** Is `id` a real, saved document id? Local drafts have placeholder ids and no backend document. */
|
|
95
106
|
declare function isDocumentId(id: string | null | undefined): id is string;
|
|
96
107
|
|
|
@@ -173,13 +184,56 @@ interface ArtifactEditSession {
|
|
|
173
184
|
*/
|
|
174
185
|
declare function useArtifactEditSession(options: ArtifactEditSessionOptions): ArtifactEditSession;
|
|
175
186
|
|
|
176
|
-
/**
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
187
|
+
/**
|
|
188
|
+
* Where the text of a stored artifact is:
|
|
189
|
+
* - `loading`: the first request is in flight (or there is no document yet);
|
|
190
|
+
* - `parsing`: the backend has not finished parsing the upload (the endpoint answers 404, 422 or
|
|
191
|
+
* 500, or an empty parse, for a few seconds after an upload) and the hook is retrying;
|
|
192
|
+
* - `ready`: the text is here (an empty document is `ready` with `text: null`);
|
|
193
|
+
* - `error`: the retries ran out, or the request was refused (401/403).
|
|
194
|
+
*/
|
|
195
|
+
type ArtifactTextStatus = 'loading' | 'parsing' | 'ready' | 'error';
|
|
196
|
+
/** How long `useArtifactText` keeps asking while a document is still being parsed. */
|
|
197
|
+
interface ArtifactTextRetry {
|
|
198
|
+
/** Retries after the first request. Default 12. */
|
|
199
|
+
attempts?: number;
|
|
200
|
+
/** Delay before the first retry; it grows by 1.6x each time. Default 1000 ms. */
|
|
201
|
+
baseDelayMs?: number;
|
|
202
|
+
/** Cap for one delay. Default 5000 ms (12 retries are about 50 s in all). */
|
|
203
|
+
maxDelayMs?: number;
|
|
204
|
+
}
|
|
205
|
+
declare const DEFAULT_ARTIFACT_TEXT_RETRY: Required<ArtifactTextRetry>;
|
|
206
|
+
interface ArtifactText {
|
|
207
|
+
/**
|
|
208
|
+
* The markdown, clean: `undefined` while `loading` or `parsing`, `null` when there is none (an
|
|
209
|
+
* empty document, or `error`).
|
|
210
|
+
*/
|
|
180
211
|
text: string | null | undefined;
|
|
212
|
+
/** `status` is `loading` or `parsing`. */
|
|
181
213
|
isLoading: boolean;
|
|
182
|
-
|
|
214
|
+
status: ArtifactTextStatus;
|
|
215
|
+
/** The last request error while `parsing` or after `error`. */
|
|
216
|
+
error: Error | null;
|
|
217
|
+
/** Ask again now (after an `error`). */
|
|
218
|
+
retry: () => void;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* The saved markdown of an artifact version (its parsed chunks, joined and unescaped).
|
|
222
|
+
*
|
|
223
|
+
* A freshly uploaded document is parsed asynchronously, and for roughly 3 to 20 seconds the parsed
|
|
224
|
+
* content endpoint answers 404, 422 or 500 (or an empty parse). The hook therefore retries with a
|
|
225
|
+
* growing delay (see `ArtifactTextRetry`) and reports `status: 'parsing'` meanwhile, so a host can
|
|
226
|
+
* show "Preparing editor..." instead of opening an editor on an empty baseline. After the last
|
|
227
|
+
* retry the status is `error`; `retry()` starts a new round.
|
|
228
|
+
*
|
|
229
|
+
* The text is run through `unescapeMarkdown`: an artifact is always stored from markdown, and the
|
|
230
|
+
* backend's parse of a `.md` escapes its punctuation.
|
|
231
|
+
*/
|
|
232
|
+
declare function useArtifactText(docId: string | null | undefined, options?: {
|
|
233
|
+
enabled?: boolean;
|
|
234
|
+
retry?: ArtifactTextRetry;
|
|
235
|
+
}): ArtifactText;
|
|
236
|
+
|
|
183
237
|
/** The version lineage of an artifact in a workspace, oldest first. */
|
|
184
238
|
declare function useArtifactVersions(workspaceId: string | undefined, docId: string | null | undefined): ArtifactVersion[];
|
|
185
239
|
interface SaveArtifactVersionInput {
|
|
@@ -236,6 +290,13 @@ interface ArtifactSessionView {
|
|
|
236
290
|
onCompare: () => void;
|
|
237
291
|
onStopCompare: () => void;
|
|
238
292
|
onToggleTrackChanges: () => void;
|
|
293
|
+
/**
|
|
294
|
+
* Whether the text is ready. `ArtifactEditor` renders "Preparing editor..." while it is
|
|
295
|
+
* `loading` or `parsing` (a fresh upload is parsed for a few seconds) and an error with Retry.
|
|
296
|
+
*/
|
|
297
|
+
status: ArtifactTextStatus;
|
|
298
|
+
/** Ask for the text again after an `error`. */
|
|
299
|
+
onRetry: () => void;
|
|
239
300
|
}
|
|
240
301
|
interface ArtifactSession extends ArtifactEditSession {
|
|
241
302
|
/** The open document: the new real id once a local draft has been saved. */
|
|
@@ -243,6 +304,8 @@ interface ArtifactSession extends ArtifactEditSession {
|
|
|
243
304
|
/** The open version is an unsaved local draft (no backend document yet). */
|
|
244
305
|
isLocal: boolean;
|
|
245
306
|
isLoading: boolean;
|
|
307
|
+
/** See `ArtifactTextStatus`. */
|
|
308
|
+
status: ArtifactTextStatus;
|
|
246
309
|
versions: ArtifactVersion[];
|
|
247
310
|
/** "v2" for any version in the lineage. */
|
|
248
311
|
labelOf: (id: string | null | undefined) => string | null;
|
|
@@ -289,4 +352,53 @@ interface ArtifactRedlineComponents {
|
|
|
289
352
|
*/
|
|
290
353
|
declare function renderArtifactMarkdown(markdown: string, components?: ArtifactRedlineComponents): ReactNode;
|
|
291
354
|
|
|
292
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Where a drafted artifact is in its STREAM -> PERSIST -> EDIT life:
|
|
357
|
+
* - `streaming`: the agent is still working (or has produced nothing final): show the live canvas;
|
|
358
|
+
* - `persisting`: the finished draft is being stored as version 1 of the artifact;
|
|
359
|
+
* - `parsing`: stored; the backend is still parsing it, so there is no editable text yet;
|
|
360
|
+
* - `ready`: the text is readable: mount the editor on `docId`;
|
|
361
|
+
* - `error`: storing failed or the parse never produced text (`retry()` starts over).
|
|
362
|
+
*/
|
|
363
|
+
type StreamedArtifactPhase = 'streaming' | 'persisting' | 'parsing' | 'ready' | 'error';
|
|
364
|
+
interface UseStreamedArtifactOptions {
|
|
365
|
+
/** The artifacts of the agent run (`useConnectedAiTask().artifacts`). */
|
|
366
|
+
artifacts: ArtifactEvent[];
|
|
367
|
+
workspaceId: string | undefined;
|
|
368
|
+
/**
|
|
369
|
+
* The run has stopped: not busy and not failed. Nothing is stored before this, so a draft that
|
|
370
|
+
* is still streaming is never persisted half-written.
|
|
371
|
+
*/
|
|
372
|
+
settled: boolean;
|
|
373
|
+
/** Make the stored markdown from the streamed content. Defaults to `cleanDraftMarkdown`. */
|
|
374
|
+
clean?: (streamed: string) => string;
|
|
375
|
+
/** Processing config for the stored version. */
|
|
376
|
+
configId?: string | null;
|
|
377
|
+
/** How long to wait for the parse (see `useArtifactText`). */
|
|
378
|
+
parseRetry?: ArtifactTextRetry;
|
|
379
|
+
}
|
|
380
|
+
interface StreamedArtifact {
|
|
381
|
+
phase: StreamedArtifactPhase;
|
|
382
|
+
/** The finished streamed artifact (its `title` names the stored versions). */
|
|
383
|
+
artifact: ArtifactEvent | undefined;
|
|
384
|
+
/** The backend document to edit, once there is one (while `parsing` and `ready`). */
|
|
385
|
+
docId: string | undefined;
|
|
386
|
+
error: Error | null;
|
|
387
|
+
/** After an `error`: store again or ask for the parse again. */
|
|
388
|
+
retry: () => void;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* The lifecycle of an agent-drafted artifact, so a host passes the stream and renders the phase.
|
|
392
|
+
*
|
|
393
|
+
* While the agent streams, the draft lives only on the canvas. Once the run is `settled` and the
|
|
394
|
+
* draft is complete, a draft that is not yet a backend document is cleaned (`cleanDraftMarkdown`)
|
|
395
|
+
* and stored ONCE as the artifact's first version (`wpType: 'artifact'`); a draft that already is a
|
|
396
|
+
* backend document passes through. The hook then waits for the parse (`useArtifactText`, which
|
|
397
|
+
* retries) and reports `ready` with the `docId` to open in an editor.
|
|
398
|
+
*
|
|
399
|
+
* Needs an `<ArbiProvider>` and a workspace. `@arbidocs/blocks/connect` has the whole thing as a
|
|
400
|
+
* component (`StreamedArtifact`).
|
|
401
|
+
*/
|
|
402
|
+
declare function useStreamedArtifact({ artifacts, workspaceId, settled, clean, configId, parseRetry, }: UseStreamedArtifactOptions): StreamedArtifact;
|
|
403
|
+
|
|
404
|
+
export { type ArtifactEditSession, type ArtifactEditSessionOptions, type ArtifactRedlineComponents, type ArtifactSaveResult, type ArtifactSession, type ArtifactSessionOptions, type ArtifactSessionView, type ArtifactText, type ArtifactTextRetry, type ArtifactTextStatus, type ArtifactVersion, DEFAULT_ARTIFACT_TEXT_RETRY, type RedlineEngine, RedlineRegion, type RedlineState, RedlineWithRegions, type SaveArtifactVersionInput, type StreamedArtifact, type StreamedArtifactPhase, type UseRedlineOptions, type UseRedlineResult, type UseStreamedArtifactOptions, type VersionedDoc, buildVersionChain, flattenChunks, isDocumentId, mainThreadRedlineEngine, renderArtifactMarkdown, toArtifactVersions, useArtifactEditSession, useArtifactSession, useArtifactText, useArtifactVersions, useRedline, useSaveArtifactVersion, useStreamedArtifact, versionLabel };
|
package/dist/artifacts/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { repairParsedTitle, unescapeMarkdown, arbiQueryKeys, useDocuments, cleanDraftMarkdown } from '../chunk-QFELRTTG.js';
|
|
2
|
+
export { cleanDraftMarkdown, promoteBoldTitle, repairParsedTitle, stripAgentPreamble, unescapeMarkdown, unwrapCitationLinks } from '../chunk-QFELRTTG.js';
|
|
3
|
+
import { revertRedlineRegion, buildHybridRedlineWithRegions, buildHybridRedlineMarkdown, normalizeMarkdown } from '../chunk-43AKLNBC.js';
|
|
4
|
+
export { buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, normalizeMarkdown, revertRedlineRegion } from '../chunk-43AKLNBC.js';
|
|
4
5
|
import { MarkdownRenderer } from '../chunk-TGGQGFS3.js';
|
|
5
6
|
import '../chunk-SN5YV6FF.js';
|
|
6
7
|
import { useArbi } from '../chunk-J4MX2W4H.js';
|
|
7
8
|
import { useState, useRef, useEffect, useCallback, useMemo } from 'react';
|
|
8
|
-
import { useQueryClient, useMutation } from '@tanstack/react-query';
|
|
9
|
+
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
9
10
|
import { jsx } from 'react/jsx-runtime';
|
|
10
11
|
|
|
11
12
|
// src/artifacts/redline-engine.ts
|
|
@@ -36,7 +37,7 @@ function useRedline({
|
|
|
36
37
|
setState({ kind: "unavailable" });
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
|
-
if (baseText === compareText) {
|
|
40
|
+
if (baseText === compareText || normalizeMarkdown(baseText) === normalizeMarkdown(compareText)) {
|
|
40
41
|
setState({ kind: "identical" });
|
|
41
42
|
return;
|
|
42
43
|
}
|
|
@@ -69,7 +70,7 @@ function useRedline({
|
|
|
69
70
|
if (!base || !compare) return null;
|
|
70
71
|
try {
|
|
71
72
|
const next = await engine.revertRegion(base, compare, regionId);
|
|
72
|
-
return next !== compare ? next : null;
|
|
73
|
+
return next !== normalizeMarkdown(compare) ? next : null;
|
|
73
74
|
} catch {
|
|
74
75
|
return null;
|
|
75
76
|
}
|
|
@@ -129,8 +130,9 @@ function versionLabel(versions, docId) {
|
|
|
129
130
|
const index = versions.findIndex((v) => v.id === docId);
|
|
130
131
|
return index === -1 ? null : `v${index + 1}`;
|
|
131
132
|
}
|
|
132
|
-
function flattenChunks(chunks) {
|
|
133
|
-
|
|
133
|
+
function flattenChunks(chunks, options) {
|
|
134
|
+
const text = (chunks ?? []).map((c) => (c.content ?? "").trim()).join("\n\n");
|
|
135
|
+
return options?.unescape ? repairParsedTitle(unescapeMarkdown(text)) : text;
|
|
134
136
|
}
|
|
135
137
|
var DOC_ID = /^doc/;
|
|
136
138
|
function isDocumentId(id) {
|
|
@@ -216,7 +218,9 @@ function useArtifactEditSession(options) {
|
|
|
216
218
|
setShowTrackChanges(true);
|
|
217
219
|
}, [compareId, compareOpensEditor, hasBaseline, startEdit]);
|
|
218
220
|
const save = useCallback(async () => {
|
|
219
|
-
const
|
|
221
|
+
const raw = latest.current.buffer;
|
|
222
|
+
const normalised = normalizeMarkdown(raw);
|
|
223
|
+
const content2 = raw.endsWith("\n") ? normalised : normalised.replace(/\n$/, "");
|
|
220
224
|
setIsSaving(true);
|
|
221
225
|
try {
|
|
222
226
|
const result = await persist(content2) ?? {};
|
|
@@ -311,19 +315,71 @@ function useArtifactEditSession(options) {
|
|
|
311
315
|
]
|
|
312
316
|
);
|
|
313
317
|
}
|
|
318
|
+
var DEFAULT_ARTIFACT_TEXT_RETRY = {
|
|
319
|
+
attempts: 12,
|
|
320
|
+
baseDelayMs: 1e3,
|
|
321
|
+
maxDelayMs: 5e3
|
|
322
|
+
};
|
|
323
|
+
var NotParsedYetError = class extends Error {
|
|
324
|
+
constructor() {
|
|
325
|
+
super("The document has no parsed text yet");
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
var isRefusal = (error) => {
|
|
329
|
+
const status = error;
|
|
330
|
+
const code = status?.status ?? status?.response?.status;
|
|
331
|
+
return code === 401 || code === 403;
|
|
332
|
+
};
|
|
314
333
|
function useArtifactText(docId, options) {
|
|
315
|
-
const
|
|
316
|
-
const {
|
|
317
|
-
|
|
334
|
+
const arbi = useArbi();
|
|
335
|
+
const { attempts, baseDelayMs, maxDelayMs } = {
|
|
336
|
+
...DEFAULT_ARTIFACT_TEXT_RETRY,
|
|
337
|
+
...options?.retry
|
|
338
|
+
};
|
|
339
|
+
const enabled = (options?.enabled ?? true) && isDocumentId(docId) && arbi.hasWorkspace;
|
|
340
|
+
const query = useQuery({
|
|
341
|
+
// Under the shared `parsedDoc` key, so a document's invalidation reaches it too.
|
|
342
|
+
queryKey: [...arbiQueryKeys.parsedDoc(docId ?? "", "marker"), "artifact-text"],
|
|
343
|
+
queryFn: async () => {
|
|
344
|
+
const parsed = await arbi.documents.getParsedContent(docId, "marker");
|
|
345
|
+
if (!parsed.chunks?.some((c) => c.content?.trim())) throw new NotParsedYetError();
|
|
346
|
+
return flattenChunks(parsed.chunks, { unescape: true });
|
|
347
|
+
},
|
|
348
|
+
enabled,
|
|
349
|
+
retry: (failureCount, error) => !isRefusal(error) && failureCount < attempts,
|
|
350
|
+
retryDelay: (failureCount) => Math.min(baseDelayMs * 1.6 ** failureCount, maxDelayMs)
|
|
318
351
|
});
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
352
|
+
const { refetch } = query;
|
|
353
|
+
const retry = useCallback(() => void refetch(), [refetch]);
|
|
354
|
+
return useMemo(() => {
|
|
355
|
+
const error = query.error;
|
|
356
|
+
const neverParsed = error instanceof NotParsedYetError;
|
|
357
|
+
let status = "loading";
|
|
358
|
+
if (!enabled) status = "loading";
|
|
359
|
+
else if (query.isSuccess || query.isError && neverParsed) status = "ready";
|
|
360
|
+
else if (query.isError) status = "error";
|
|
361
|
+
else if (query.failureCount > 0) status = "parsing";
|
|
362
|
+
const text = !enabled ? void 0 : query.isSuccess ? query.data : status === "ready" || status === "error" ? null : void 0;
|
|
363
|
+
return {
|
|
364
|
+
text,
|
|
365
|
+
isLoading: enabled && (status === "loading" || status === "parsing"),
|
|
366
|
+
status,
|
|
367
|
+
error: status === "ready" ? null : error ?? query.failureReason ?? null,
|
|
368
|
+
retry
|
|
369
|
+
};
|
|
370
|
+
}, [
|
|
371
|
+
enabled,
|
|
372
|
+
query.isSuccess,
|
|
373
|
+
query.isError,
|
|
374
|
+
query.data,
|
|
375
|
+
query.error,
|
|
376
|
+
query.failureCount,
|
|
377
|
+
query.failureReason,
|
|
378
|
+
retry
|
|
379
|
+
]);
|
|
326
380
|
}
|
|
381
|
+
|
|
382
|
+
// src/artifacts/useArtifactSession.ts
|
|
327
383
|
function useArtifactVersions(workspaceId, docId) {
|
|
328
384
|
const { data: documents } = useDocuments(workspaceId);
|
|
329
385
|
return useMemo(
|
|
@@ -378,7 +434,7 @@ function useArtifactSession(options) {
|
|
|
378
434
|
},
|
|
379
435
|
[onCompareChange]
|
|
380
436
|
);
|
|
381
|
-
const { text, isLoading } = useArtifactText(docId);
|
|
437
|
+
const { text, isLoading, status, retry } = useArtifactText(docId);
|
|
382
438
|
const versions = useArtifactVersions(workspaceId, isLocal ? null : docId);
|
|
383
439
|
const { text: compareText, isLoading: compareLoading } = useArtifactText(activeCompareId, {
|
|
384
440
|
enabled: !!activeCompareId
|
|
@@ -427,6 +483,7 @@ function useArtifactSession(options) {
|
|
|
427
483
|
docId,
|
|
428
484
|
isLocal,
|
|
429
485
|
isLoading,
|
|
486
|
+
status,
|
|
430
487
|
versions,
|
|
431
488
|
labelOf: (id) => versionLabel(versions, id),
|
|
432
489
|
view: {
|
|
@@ -445,7 +502,10 @@ function useArtifactSession(options) {
|
|
|
445
502
|
onSave: () => void session.save(),
|
|
446
503
|
onCompare: () => session.compare(),
|
|
447
504
|
onStopCompare: session.stopCompare,
|
|
448
|
-
onToggleTrackChanges: () => session.setShowTrackChanges(!session.showTrackChanges)
|
|
505
|
+
onToggleTrackChanges: () => session.setShowTrackChanges(!session.showTrackChanges),
|
|
506
|
+
// A local draft has its text already: it is ready.
|
|
507
|
+
status: isLocal ? "ready" : status,
|
|
508
|
+
onRetry: retry
|
|
449
509
|
},
|
|
450
510
|
editorProps: {
|
|
451
511
|
markdown: session.content,
|
|
@@ -457,13 +517,64 @@ function useArtifactSession(options) {
|
|
|
457
517
|
onRevert: (regionId) => void session.revertRegion(regionId)
|
|
458
518
|
}
|
|
459
519
|
}),
|
|
460
|
-
[session, redline, isLoading, isLocal, versions, docId]
|
|
520
|
+
[session, redline, isLoading, status, retry, isLocal, versions, docId]
|
|
461
521
|
);
|
|
462
522
|
}
|
|
463
523
|
function renderArtifactMarkdown(markdown, components) {
|
|
464
524
|
return /* @__PURE__ */ jsx(MarkdownRenderer, { components, children: markdown });
|
|
465
525
|
}
|
|
526
|
+
function useStreamedArtifact({
|
|
527
|
+
artifacts,
|
|
528
|
+
workspaceId,
|
|
529
|
+
settled,
|
|
530
|
+
clean = cleanDraftMarkdown,
|
|
531
|
+
configId,
|
|
532
|
+
parseRetry
|
|
533
|
+
}) {
|
|
534
|
+
const artifact = artifacts.find((a) => a.is_complete);
|
|
535
|
+
const { mutateAsync } = useSaveArtifactVersion();
|
|
536
|
+
const [saved, setSaved] = useState(null);
|
|
537
|
+
const [saveError, setSaveError] = useState(null);
|
|
538
|
+
const [attempt, setAttempt] = useState(0);
|
|
539
|
+
const started = useRef(null);
|
|
540
|
+
const content = artifact?.content ?? "";
|
|
541
|
+
const title = artifact?.title ?? "Draft";
|
|
542
|
+
const passThrough = !!artifact && isDocumentId(artifact.doc_ext_id);
|
|
543
|
+
const needsSave = settled && !!artifact && !passThrough && !!content.trim() && !!workspaceId;
|
|
544
|
+
useEffect(() => {
|
|
545
|
+
const key = `${attempt}:${content}`;
|
|
546
|
+
if (!needsSave || started.current === key) return;
|
|
547
|
+
started.current = key;
|
|
548
|
+
setSaveError(null);
|
|
549
|
+
mutateAsync({
|
|
550
|
+
title,
|
|
551
|
+
content: clean(content),
|
|
552
|
+
workspaceId,
|
|
553
|
+
configId,
|
|
554
|
+
wpType: "artifact"
|
|
555
|
+
}).then((r) => {
|
|
556
|
+
if (r.docId) setSaved({ content, docId: r.docId });
|
|
557
|
+
else setSaveError(new Error("The draft was stored but the server returned no document id"));
|
|
558
|
+
}).catch((e) => setSaveError(e instanceof Error ? e : new Error(String(e))));
|
|
559
|
+
}, [needsSave, content, title, workspaceId, configId, attempt, mutateAsync]);
|
|
560
|
+
const docId = !artifact ? void 0 : passThrough ? artifact.doc_ext_id : saved?.content === content ? saved.docId : void 0;
|
|
561
|
+
const text = useArtifactText(docId, { enabled: settled, retry: parseRetry });
|
|
562
|
+
const { retry: retryText } = text;
|
|
563
|
+
const retry = useCallback(() => {
|
|
564
|
+
if (saveError) setAttempt((n) => n + 1);
|
|
565
|
+
else retryText();
|
|
566
|
+
}, [saveError, retryText]);
|
|
567
|
+
let phase;
|
|
568
|
+
if (!settled || !artifact) phase = "streaming";
|
|
569
|
+
else if (saveError) phase = "error";
|
|
570
|
+
else if (!docId) phase = needsSave ? "persisting" : "streaming";
|
|
571
|
+
else if (text.status === "ready" && text.text) phase = "ready";
|
|
572
|
+
else if (text.status === "error" || text.status === "ready") phase = "error";
|
|
573
|
+
else phase = "parsing";
|
|
574
|
+
const error = phase !== "error" ? null : saveError ?? text.error ?? new Error("The document has no text to edit");
|
|
575
|
+
return { phase, artifact, docId, error, retry };
|
|
576
|
+
}
|
|
466
577
|
|
|
467
|
-
export { buildVersionChain, flattenChunks, isDocumentId, mainThreadRedlineEngine, renderArtifactMarkdown, toArtifactVersions, useArtifactEditSession, useArtifactSession, useArtifactText, useArtifactVersions, useRedline, useSaveArtifactVersion, versionLabel };
|
|
578
|
+
export { DEFAULT_ARTIFACT_TEXT_RETRY, buildVersionChain, flattenChunks, isDocumentId, mainThreadRedlineEngine, renderArtifactMarkdown, toArtifactVersions, useArtifactEditSession, useArtifactSession, useArtifactText, useArtifactVersions, useRedline, useSaveArtifactVersion, useStreamedArtifact, versionLabel };
|
|
468
579
|
//# sourceMappingURL=index.js.map
|
|
469
580
|
//# sourceMappingURL=index.js.map
|