@arbidocs/react 0.4.8 → 0.4.10
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 +155 -21
- package/dist/artifacts/index.cjs.map +1 -1
- package/dist/artifacts/index.d.cts +120 -8
- package/dist/artifacts/index.d.ts +120 -8
- package/dist/artifacts/index.js +129 -20
- package/dist/artifacts/index.js.map +1 -1
- package/dist/{chunk-YURJV3RS.cjs → chunk-K5TOB34Q.cjs} +4 -4
- package/dist/{chunk-YURJV3RS.cjs.map → chunk-K5TOB34Q.cjs.map} +1 -1
- package/dist/{chunk-CBJ3WOGD.cjs → chunk-KKSSTNJK.cjs} +123 -3
- package/dist/chunk-KKSSTNJK.cjs.map +1 -0
- package/dist/{chunk-U7RBOJK3.js → chunk-NVMQLJI4.js} +3 -3
- package/dist/{chunk-U7RBOJK3.js.map → chunk-NVMQLJI4.js.map} +1 -1
- package/dist/{chunk-77G7DSUW.js → chunk-TGGQGFS3.js} +57 -6
- package/dist/chunk-TGGQGFS3.js.map +1 -0
- package/dist/{chunk-KWCHIAQ5.js → chunk-UV5ZEUNV.js} +119 -5
- package/dist/chunk-UV5ZEUNV.js.map +1 -0
- package/dist/{chunk-FUPWPQGK.cjs → chunk-WBWXP2DN.cjs} +56 -5
- package/dist/chunk-WBWXP2DN.cjs.map +1 -0
- package/dist/index.cjs +395 -366
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +16 -11
- package/dist/index.js.map +1 -1
- package/dist/stored-markdown-DMwFzWhc.d.cts +75 -0
- package/dist/stored-markdown-DMwFzWhc.d.ts +75 -0
- package/dist/ui/index.cjs +147 -147
- package/dist/ui/index.js +2 -2
- package/package.json +3 -3
- package/dist/chunk-77G7DSUW.js.map +0 -1
- package/dist/chunk-CBJ3WOGD.cjs.map +0 -1
- package/dist/chunk-FUPWPQGK.cjs.map +0 -1
- package/dist/chunk-KWCHIAQ5.js.map +0 -1
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { RedlineWithRegions, RedlineRegion } from './redline-core.cjs';
|
|
2
2
|
export { RegionKind, buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, 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
2
|
export { RegionKind, buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, 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 {
|
|
1
|
+
import { repairParsedTitle, unescapeMarkdown, arbiQueryKeys, useDocuments, cleanDraftMarkdown } from '../chunk-UV5ZEUNV.js';
|
|
2
|
+
export { cleanDraftMarkdown, promoteBoldTitle, repairParsedTitle, stripAgentPreamble, unescapeMarkdown, unwrapCitationLinks } from '../chunk-UV5ZEUNV.js';
|
|
2
3
|
import { revertRedlineRegion, buildHybridRedlineWithRegions, buildHybridRedlineMarkdown } from '../chunk-RKWNX32A.js';
|
|
3
4
|
export { buildHybridRedlineMarkdown, buildHybridRedlineWithRegions, revertRedlineRegion } from '../chunk-RKWNX32A.js';
|
|
4
|
-
import { MarkdownRenderer } from '../chunk-77G7DSUW.js';
|
|
5
|
-
import '../chunk-SN5YV6FF.js';
|
|
6
5
|
import { useArbi } from '../chunk-J4MX2W4H.js';
|
|
6
|
+
import { MarkdownRenderer } from '../chunk-TGGQGFS3.js';
|
|
7
|
+
import '../chunk-SN5YV6FF.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
|
|
@@ -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) {
|
|
@@ -311,19 +313,71 @@ function useArtifactEditSession(options) {
|
|
|
311
313
|
]
|
|
312
314
|
);
|
|
313
315
|
}
|
|
316
|
+
var DEFAULT_ARTIFACT_TEXT_RETRY = {
|
|
317
|
+
attempts: 12,
|
|
318
|
+
baseDelayMs: 1e3,
|
|
319
|
+
maxDelayMs: 5e3
|
|
320
|
+
};
|
|
321
|
+
var NotParsedYetError = class extends Error {
|
|
322
|
+
constructor() {
|
|
323
|
+
super("The document has no parsed text yet");
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
var isRefusal = (error) => {
|
|
327
|
+
const status = error;
|
|
328
|
+
const code = status?.status ?? status?.response?.status;
|
|
329
|
+
return code === 401 || code === 403;
|
|
330
|
+
};
|
|
314
331
|
function useArtifactText(docId, options) {
|
|
315
|
-
const
|
|
316
|
-
const {
|
|
317
|
-
|
|
332
|
+
const arbi = useArbi();
|
|
333
|
+
const { attempts, baseDelayMs, maxDelayMs } = {
|
|
334
|
+
...DEFAULT_ARTIFACT_TEXT_RETRY,
|
|
335
|
+
...options?.retry
|
|
336
|
+
};
|
|
337
|
+
const enabled = (options?.enabled ?? true) && isDocumentId(docId) && arbi.hasWorkspace;
|
|
338
|
+
const query = useQuery({
|
|
339
|
+
// Under the shared `parsedDoc` key, so a document's invalidation reaches it too.
|
|
340
|
+
queryKey: [...arbiQueryKeys.parsedDoc(docId ?? "", "marker"), "artifact-text"],
|
|
341
|
+
queryFn: async () => {
|
|
342
|
+
const parsed = await arbi.documents.getParsedContent(docId, "marker");
|
|
343
|
+
if (!parsed.chunks?.some((c) => c.content?.trim())) throw new NotParsedYetError();
|
|
344
|
+
return flattenChunks(parsed.chunks, { unescape: true });
|
|
345
|
+
},
|
|
346
|
+
enabled,
|
|
347
|
+
retry: (failureCount, error) => !isRefusal(error) && failureCount < attempts,
|
|
348
|
+
retryDelay: (failureCount) => Math.min(baseDelayMs * 1.6 ** failureCount, maxDelayMs)
|
|
318
349
|
});
|
|
319
|
-
const
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
350
|
+
const { refetch } = query;
|
|
351
|
+
const retry = useCallback(() => void refetch(), [refetch]);
|
|
352
|
+
return useMemo(() => {
|
|
353
|
+
const error = query.error;
|
|
354
|
+
const neverParsed = error instanceof NotParsedYetError;
|
|
355
|
+
let status = "loading";
|
|
356
|
+
if (!enabled) status = "loading";
|
|
357
|
+
else if (query.isSuccess || query.isError && neverParsed) status = "ready";
|
|
358
|
+
else if (query.isError) status = "error";
|
|
359
|
+
else if (query.failureCount > 0) status = "parsing";
|
|
360
|
+
const text = !enabled ? void 0 : query.isSuccess ? query.data : status === "ready" || status === "error" ? null : void 0;
|
|
361
|
+
return {
|
|
362
|
+
text,
|
|
363
|
+
isLoading: enabled && (status === "loading" || status === "parsing"),
|
|
364
|
+
status,
|
|
365
|
+
error: status === "ready" ? null : error ?? query.failureReason ?? null,
|
|
366
|
+
retry
|
|
367
|
+
};
|
|
368
|
+
}, [
|
|
369
|
+
enabled,
|
|
370
|
+
query.isSuccess,
|
|
371
|
+
query.isError,
|
|
372
|
+
query.data,
|
|
373
|
+
query.error,
|
|
374
|
+
query.failureCount,
|
|
375
|
+
query.failureReason,
|
|
376
|
+
retry
|
|
377
|
+
]);
|
|
326
378
|
}
|
|
379
|
+
|
|
380
|
+
// src/artifacts/useArtifactSession.ts
|
|
327
381
|
function useArtifactVersions(workspaceId, docId) {
|
|
328
382
|
const { data: documents } = useDocuments(workspaceId);
|
|
329
383
|
return useMemo(
|
|
@@ -378,7 +432,7 @@ function useArtifactSession(options) {
|
|
|
378
432
|
},
|
|
379
433
|
[onCompareChange]
|
|
380
434
|
);
|
|
381
|
-
const { text, isLoading } = useArtifactText(docId);
|
|
435
|
+
const { text, isLoading, status, retry } = useArtifactText(docId);
|
|
382
436
|
const versions = useArtifactVersions(workspaceId, isLocal ? null : docId);
|
|
383
437
|
const { text: compareText, isLoading: compareLoading } = useArtifactText(activeCompareId, {
|
|
384
438
|
enabled: !!activeCompareId
|
|
@@ -427,6 +481,7 @@ function useArtifactSession(options) {
|
|
|
427
481
|
docId,
|
|
428
482
|
isLocal,
|
|
429
483
|
isLoading,
|
|
484
|
+
status,
|
|
430
485
|
versions,
|
|
431
486
|
labelOf: (id) => versionLabel(versions, id),
|
|
432
487
|
view: {
|
|
@@ -445,7 +500,10 @@ function useArtifactSession(options) {
|
|
|
445
500
|
onSave: () => void session.save(),
|
|
446
501
|
onCompare: () => session.compare(),
|
|
447
502
|
onStopCompare: session.stopCompare,
|
|
448
|
-
onToggleTrackChanges: () => session.setShowTrackChanges(!session.showTrackChanges)
|
|
503
|
+
onToggleTrackChanges: () => session.setShowTrackChanges(!session.showTrackChanges),
|
|
504
|
+
// A local draft has its text already: it is ready.
|
|
505
|
+
status: isLocal ? "ready" : status,
|
|
506
|
+
onRetry: retry
|
|
449
507
|
},
|
|
450
508
|
editorProps: {
|
|
451
509
|
markdown: session.content,
|
|
@@ -457,13 +515,64 @@ function useArtifactSession(options) {
|
|
|
457
515
|
onRevert: (regionId) => void session.revertRegion(regionId)
|
|
458
516
|
}
|
|
459
517
|
}),
|
|
460
|
-
[session, redline, isLoading, isLocal, versions, docId]
|
|
518
|
+
[session, redline, isLoading, status, retry, isLocal, versions, docId]
|
|
461
519
|
);
|
|
462
520
|
}
|
|
463
521
|
function renderArtifactMarkdown(markdown, components) {
|
|
464
522
|
return /* @__PURE__ */ jsx(MarkdownRenderer, { components, children: markdown });
|
|
465
523
|
}
|
|
524
|
+
function useStreamedArtifact({
|
|
525
|
+
artifacts,
|
|
526
|
+
workspaceId,
|
|
527
|
+
settled,
|
|
528
|
+
clean = cleanDraftMarkdown,
|
|
529
|
+
configId,
|
|
530
|
+
parseRetry
|
|
531
|
+
}) {
|
|
532
|
+
const artifact = artifacts.find((a) => a.is_complete);
|
|
533
|
+
const { mutateAsync } = useSaveArtifactVersion();
|
|
534
|
+
const [saved, setSaved] = useState(null);
|
|
535
|
+
const [saveError, setSaveError] = useState(null);
|
|
536
|
+
const [attempt, setAttempt] = useState(0);
|
|
537
|
+
const started = useRef(null);
|
|
538
|
+
const content = artifact?.content ?? "";
|
|
539
|
+
const title = artifact?.title ?? "Draft";
|
|
540
|
+
const passThrough = !!artifact && isDocumentId(artifact.doc_ext_id);
|
|
541
|
+
const needsSave = settled && !!artifact && !passThrough && !!content.trim() && !!workspaceId;
|
|
542
|
+
useEffect(() => {
|
|
543
|
+
const key = `${attempt}:${content}`;
|
|
544
|
+
if (!needsSave || started.current === key) return;
|
|
545
|
+
started.current = key;
|
|
546
|
+
setSaveError(null);
|
|
547
|
+
mutateAsync({
|
|
548
|
+
title,
|
|
549
|
+
content: clean(content),
|
|
550
|
+
workspaceId,
|
|
551
|
+
configId,
|
|
552
|
+
wpType: "artifact"
|
|
553
|
+
}).then((r) => {
|
|
554
|
+
if (r.docId) setSaved({ content, docId: r.docId });
|
|
555
|
+
else setSaveError(new Error("The draft was stored but the server returned no document id"));
|
|
556
|
+
}).catch((e) => setSaveError(e instanceof Error ? e : new Error(String(e))));
|
|
557
|
+
}, [needsSave, content, title, workspaceId, configId, attempt, mutateAsync]);
|
|
558
|
+
const docId = !artifact ? void 0 : passThrough ? artifact.doc_ext_id : saved?.content === content ? saved.docId : void 0;
|
|
559
|
+
const text = useArtifactText(docId, { enabled: settled, retry: parseRetry });
|
|
560
|
+
const { retry: retryText } = text;
|
|
561
|
+
const retry = useCallback(() => {
|
|
562
|
+
if (saveError) setAttempt((n) => n + 1);
|
|
563
|
+
else retryText();
|
|
564
|
+
}, [saveError, retryText]);
|
|
565
|
+
let phase;
|
|
566
|
+
if (!settled || !artifact) phase = "streaming";
|
|
567
|
+
else if (saveError) phase = "error";
|
|
568
|
+
else if (!docId) phase = needsSave ? "persisting" : "streaming";
|
|
569
|
+
else if (text.status === "ready" && text.text) phase = "ready";
|
|
570
|
+
else if (text.status === "error" || text.status === "ready") phase = "error";
|
|
571
|
+
else phase = "parsing";
|
|
572
|
+
const error = phase !== "error" ? null : saveError ?? text.error ?? new Error("The document has no text to edit");
|
|
573
|
+
return { phase, artifact, docId, error, retry };
|
|
574
|
+
}
|
|
466
575
|
|
|
467
|
-
export { buildVersionChain, flattenChunks, isDocumentId, mainThreadRedlineEngine, renderArtifactMarkdown, toArtifactVersions, useArtifactEditSession, useArtifactSession, useArtifactText, useArtifactVersions, useRedline, useSaveArtifactVersion, versionLabel };
|
|
576
|
+
export { DEFAULT_ARTIFACT_TEXT_RETRY, buildVersionChain, flattenChunks, isDocumentId, mainThreadRedlineEngine, renderArtifactMarkdown, toArtifactVersions, useArtifactEditSession, useArtifactSession, useArtifactText, useArtifactVersions, useRedline, useSaveArtifactVersion, useStreamedArtifact, versionLabel };
|
|
468
577
|
//# sourceMappingURL=index.js.map
|
|
469
578
|
//# sourceMappingURL=index.js.map
|