@arbidocs/react 0.5.125 → 0.5.127

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.
Files changed (36) hide show
  1. package/dist/artifacts/index.cjs +81 -38
  2. package/dist/artifacts/index.cjs.map +1 -1
  3. package/dist/artifacts/index.d.ts +3 -0
  4. package/dist/artifacts/useArtifactSession.js +48 -4
  5. package/dist/artifacts/useArtifactSession.js.map +2 -2
  6. package/dist/artifacts/versions.js +2 -1
  7. package/dist/artifacts/versions.js.map +2 -2
  8. package/dist/calls/livekit/index.cjs +42 -42
  9. package/dist/chat/turn-flush.js +3 -3
  10. package/dist/chat/turn-flush.js.map +2 -2
  11. package/dist/{chunk-WUYSVTKE.cjs → chunk-6RRKO4ZK.cjs} +4 -4
  12. package/dist/{chunk-WUYSVTKE.cjs.map → chunk-6RRKO4ZK.cjs.map} +1 -1
  13. package/dist/{chunk-FAJLJDWS.cjs → chunk-FOBB5U4Y.cjs} +5 -5
  14. package/dist/{chunk-FAJLJDWS.cjs.map → chunk-FOBB5U4Y.cjs.map} +1 -1
  15. package/dist/{chunk-3JAEX57E.cjs → chunk-G5DYDBQN.cjs} +4 -4
  16. package/dist/{chunk-3JAEX57E.cjs.map → chunk-G5DYDBQN.cjs.map} +1 -1
  17. package/dist/{chunk-7L7QRIQ2.cjs → chunk-H3XHBIZ6.cjs} +5 -5
  18. package/dist/{chunk-7L7QRIQ2.cjs.map → chunk-H3XHBIZ6.cjs.map} +1 -1
  19. package/dist/{chunk-NDIKMOBJ.cjs → chunk-KULS4YWP.cjs} +4 -3
  20. package/dist/chunk-KULS4YWP.cjs.map +1 -0
  21. package/dist/{chunk-GKGAPU6T.cjs → chunk-NK5KFHXP.cjs} +17 -2
  22. package/dist/chunk-NK5KFHXP.cjs.map +1 -0
  23. package/dist/{chunk-K6ELFSG6.cjs → chunk-TNJ5T2MV.cjs} +25 -25
  24. package/dist/{chunk-K6ELFSG6.cjs.map → chunk-TNJ5T2MV.cjs.map} +1 -1
  25. package/dist/document-tools/core.cjs +97 -97
  26. package/dist/document-tools/core.d.ts +1 -0
  27. package/dist/document-tools/index.cjs +99 -99
  28. package/dist/index.cjs +809 -806
  29. package/dist/index.cjs.map +1 -1
  30. package/dist/index.d.ts +20 -11
  31. package/dist/remote-control.js +12 -2
  32. package/dist/remote-control.js.map +2 -2
  33. package/dist/shell/index.cjs +38 -38
  34. package/package.json +3 -3
  35. package/dist/chunk-GKGAPU6T.cjs.map +0 -1
  36. package/dist/chunk-NDIKMOBJ.cjs.map +0 -1
@@ -1,4 +1,5 @@
1
- import { useCallback, useMemo, useState } from "react";
1
+ import { registerDocumentTurnFlush } from "../chat/turn-flush.js";
2
+ import { useCallback, useEffect, useMemo, useState } from "react";
2
3
  import { useMutation, useQueryClient } from "@tanstack/react-query";
3
4
  import { useArbi } from "../useArbi.js";
4
5
  import { arbiQueryKeys, useDocumentVersionList } from "../hooks.js";
@@ -34,13 +35,14 @@ function useSaveArtifactVersion() {
34
35
  const arbi = useArbi();
35
36
  const queryClient = useQueryClient();
36
37
  return useMutation({
37
- mutationFn: async ({ title, content, versionOf, configId, wpType }) => {
38
+ mutationFn: async ({ title, content, versionOf, configId, wpType, autosave }) => {
38
39
  const fileName = /\.md$/i.test(title) ? title : `${title}.md`;
39
40
  const result = await arbi.documents.uploadFile(
40
41
  new Blob([content], { type: "text/markdown" }),
41
42
  fileName,
42
43
  {
43
44
  wpType: wpType ?? "artifact",
45
+ autosave,
44
46
  versionOf: versionOf ? parseVersionRef(versionOf).docId : void 0,
45
47
  configExtId: configId ?? void 0
46
48
  }
@@ -85,6 +87,7 @@ function useArtifactSession(options) {
85
87
  enabled: !!activeCompareId
86
88
  });
87
89
  const saveVersion = useSaveArtifactVersion();
90
+ const autosaveVersion = saveVersion.mutateAsync;
88
91
  const current = findVersion(versions, docId);
89
92
  const save = useCallback(
90
93
  async (content) => {
@@ -121,6 +124,36 @@ function useArtifactSession(options) {
121
124
  save,
122
125
  onSaved: handleSaved
123
126
  });
127
+ useEffect(() => {
128
+ if (isLocal || !session.hasChanges || session.isSaving) return;
129
+ const timer = setTimeout(() => {
130
+ void autosaveVersion({
131
+ title: title ?? current?.name ?? "Untitled",
132
+ content: session.content,
133
+ versionOf: docId,
134
+ configId,
135
+ autosave: true
136
+ }).catch(() => void 0);
137
+ }, 2500);
138
+ return () => clearTimeout(timer);
139
+ }, [
140
+ isLocal,
141
+ session.content,
142
+ session.hasChanges,
143
+ session.isSaving,
144
+ docId,
145
+ title,
146
+ current?.name,
147
+ configId,
148
+ autosaveVersion
149
+ ]);
150
+ useEffect(() => {
151
+ if (!docId) return;
152
+ return registerDocumentTurnFlush(docId, async () => {
153
+ if (session.hasChanges && !await session.save())
154
+ throw new Error("Save the document before sending your message.");
155
+ });
156
+ }, [docId, session]);
124
157
  const { redline } = session;
125
158
  return useMemo(
126
159
  () => ({
@@ -134,7 +167,7 @@ function useArtifactSession(options) {
134
167
  view: {
135
168
  content: session.baseline,
136
169
  isEditing: session.isEditing,
137
- hasChanges: session.hasChanges,
170
+ hasChanges: session.hasChanges || !!current?.autosave,
138
171
  isSaving: session.isSaving,
139
172
  canCompare: session.canCompare,
140
173
  comparing: !!session.compareId,
@@ -162,7 +195,18 @@ function useArtifactSession(options) {
162
195
  onRevert: (regionId) => void session.revertRegion(regionId)
163
196
  }
164
197
  }),
165
- [session, redline, isLoading, status, retry, isLocal, versions, docId]
198
+ [
199
+ session,
200
+ redline,
201
+ isLoading,
202
+ status,
203
+ retry,
204
+ isLocal,
205
+ versions,
206
+ docId,
207
+ current?.id,
208
+ current?.autosave
209
+ ]
166
210
  );
167
211
  }
168
212
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/artifacts/useArtifactSession.ts"],
4
- "sourcesContent": ["import { useCallback, useMemo, useState } from 'react'\nimport { useMutation, useQueryClient } from '@tanstack/react-query'\nimport { useArbi } from '../useArbi'\nimport { arbiQueryKeys, useDocumentVersionList } from '../hooks'\nimport { refreshDocuments, useDocuments } from '../documents/documents-data'\nimport { useArtifactText, type ArtifactTextStatus } from './useArtifactText'\nimport {\n documentVersions,\n findVersion,\n isDocumentId,\n parseVersionRef,\n toArtifactVersions,\n versionLabel,\n type ArtifactVersion,\n} from './versions'\nimport {\n useArtifactEditSession,\n type ArtifactEditSession,\n type ArtifactEditSessionOptions,\n type ArtifactSaveResult,\n} from './useArtifactEditSession'\n\n/**\n * The versions of an artifact, oldest first. `docId` is a document id or a version reference\n * (`doc-\u2026@v2`). Every listed document carries its versions; a document that is not in the\n * workspace list asks for its version list.\n */\nexport function useArtifactVersions(\n workspaceId: string | undefined,\n docId: string | null | undefined\n): ArtifactVersion[] {\n const plainId = docId ? parseVersionRef(docId).docId : null\n const { data: documents } = useDocuments(workspaceId)\n const listed = useMemo(\n () => (documents ?? []).find((d) => d.external_id === plainId),\n [documents, plainId]\n )\n // Asked only once the list has loaded and the document is not in it.\n const { data: history } = useDocumentVersionList(plainId, {\n enabled: !!plainId && documents !== undefined && !listed,\n })\n return useMemo(\n () => (listed ? documentVersions(listed) : toArtifactVersions(plainId, history)),\n [listed, history, plainId]\n )\n}\n\nexport interface SaveArtifactVersionInput {\n /** File name of the new version (`.md` is added when missing). */\n title: string\n content: string\n /** Save as the next version of this document (it keeps its id); omit for a new document. */\n versionOf?: string | null\n /** The processing config (auto-title, parsing) to run the upload with. */\n configId?: string | null\n /** `artifact` (default) for AI-style drafts; `source` for a document the user typed. */\n wpType?: 'artifact' | 'source'\n}\n\n/**\n * Save markdown as a NEW immutable version (the next version of `versionOf`'s document, which keeps\n * its id) and refresh the workspace document list, version lists and the document's text.\n */\nexport function useSaveArtifactVersion() {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useMutation<ArtifactSaveResult, Error, SaveArtifactVersionInput>({\n mutationFn: async ({ title, content, versionOf, configId, wpType }) => {\n // The backend classifies uploads by extension: a title without one is silently dropped.\n const fileName = /\\.md$/i.test(title) ? title : `${title}.md`\n const result = await arbi.documents.uploadFile(\n new Blob([content], { type: 'text/markdown' }),\n fileName,\n {\n wpType: wpType ?? 'artifact',\n versionOf: versionOf ? parseVersionRef(versionOf).docId : undefined,\n configExtId: configId ?? undefined,\n }\n )\n const docId = result.doc_ext_ids?.[0]\n // The document's row moves to the new version (its version lists re-read with it).\n if (docId) await refreshDocuments(queryClient, arbi, [docId])\n await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.allDocumentVersions() })\n // The document's current text is the new version's now.\n if (docId)\n await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.parsedDocument(docId) })\n return { docId, version: result.version ?? undefined }\n },\n })\n}\n\nexport interface ArtifactSessionOptions extends Pick<\n ArtifactEditSessionOptions,\n | 'onSaved'\n | 'onSaveError'\n | 'onCompareChange'\n | 'compareId'\n | 'compareOpensEditor'\n | 'redlineEngine'\n | 'onDirtyChange'\n> {\n workspaceId: string | undefined\n /**\n * The open document (its current version), a version reference `doc-\u2026@v{n}`, or the placeholder\n * id of an unsaved local draft (anything that is not a backend document id, i.e. does not start\n * with `doc`).\n */\n docId: string | null | undefined\n /**\n * The text of a local draft (nothing is fetched for it). The FIRST save uploads it as a real\n * document with no parent and the session switches to the new id; later saves chain from it.\n */\n draftContent?: string\n /** Name for saved versions. Defaults to the open version's file name, else \"Untitled\". */\n title?: string\n /** Processing config for saved versions. */\n configId?: string | null\n}\n\n/** Everything `ArtifactEditor` (`@arbidocs/blocks/artifacts`) takes from a session, flat. */\nexport interface ArtifactSessionView {\n content: string\n isEditing: boolean\n hasChanges: boolean\n isSaving: boolean\n canCompare: boolean\n comparing: boolean\n compareId: string | null\n showTrackChanges: boolean\n versions: ArtifactVersion[]\n currentVersionId: string\n onStartEdit: () => void\n onCancel: () => void\n onSave: () => void\n onCompare: () => void\n onStopCompare: () => void\n onToggleTrackChanges: () => void\n /**\n * Whether the text is ready. `ArtifactEditor` renders \"Preparing editor...\" while it is\n * `loading` or `parsing` (a fresh upload is parsed for a few seconds) and an error with Retry.\n */\n status: ArtifactTextStatus\n /** Ask for the text again after an `error`. */\n onRetry: () => void\n}\n\nexport interface ArtifactSession extends ArtifactEditSession {\n /** The open document: the new real id once a local draft has been saved. */\n docId: string | null | undefined\n /** The open version is an unsaved local draft (no backend document yet). */\n isLocal: boolean\n isLoading: boolean\n /** See `ArtifactTextStatus`. */\n status: ArtifactTextStatus\n versions: ArtifactVersion[]\n /** \"v2\" for any version (reference) of the document. */\n labelOf: (id: string | null | undefined) => string | null\n /** Props for `<ArtifactEditor>`. */\n view: ArtifactSessionView\n /** Props for `<ArtifactMarkdownEditor>` (`@arbidocs/blocks/artifacts/editor`). */\n editorProps: {\n markdown: string\n onChange: (markdown: string) => void\n onNormalized: (markdown: string) => void\n }\n /** Props for `<ArtifactRedlineView>`. */\n redlineProps: {\n state: ArtifactEditSession['redline']['state']\n onRevert: (regionId: string) => void\n }\n}\n\n/**\n * Edit an artifact through the SDK: load it, edit it, save it as a new version, list its versions\n * and compare any two with track changes. Needs an `<ArbiHost>` with an `arbi` and a selected workspace.\n *\n * ```tsx\n * const session = useArtifactSession({ workspaceId, docId, onSaved: ({ docId }) => docId && open(docId) })\n * <ArtifactEditor {...session.view} onOpenVersion={open}\n * editor={<ArtifactMarkdownEditor {...session.editorProps} />}\n * redline={<ArtifactRedlineView {...session.redlineProps} />} />\n * ```\n */\nexport function useArtifactSession(options: ArtifactSessionOptions): ArtifactSession {\n const {\n workspaceId,\n docId: hostDocId,\n draftContent,\n title,\n configId,\n compareId,\n onCompareChange,\n onSaved,\n ...passthrough\n } = options\n // A saved local draft becomes this real document; another host docId resets it.\n const [saved, setSaved] = useState<{\n forId: string | null | undefined\n id: string\n text: string\n } | null>(null)\n const adopted = saved && saved.forId === hostDocId ? saved : null\n const docId = adopted?.id ?? hostDocId\n const isLocal = !isDocumentId(docId)\n\n // Owned here (not in the edit session) because the compare target's text must be loaded first.\n const [ownCompareId, setOwnCompareId] = useState<string | null>(null)\n const activeCompareId = compareId === undefined ? ownCompareId : compareId\n const changeCompare = useCallback(\n (id: string | null) => {\n setOwnCompareId(id)\n onCompareChange?.(id)\n },\n [onCompareChange]\n )\n\n const { text, isLoading, status, retry } = useArtifactText(docId)\n const versions = useArtifactVersions(workspaceId, isLocal ? null : docId)\n const { text: compareText, isLoading: compareLoading } = useArtifactText(activeCompareId, {\n enabled: !!activeCompareId,\n })\n const saveVersion = useSaveArtifactVersion()\n const current = findVersion(versions, docId)\n\n const save = useCallback(\n async (content: string) => {\n const result = await saveVersion.mutateAsync({\n title: title ?? current?.name ?? 'Untitled',\n content,\n versionOf: isLocal ? null : docId,\n configId,\n })\n const latest = versions.reduce((n, v) => Math.max(n, v.number), 0)\n return { ...result, version: result.version ?? (isLocal ? 1 : latest + 1) }\n },\n [saveVersion, title, current?.name, isLocal, docId, configId, versions]\n )\n\n const handleSaved = useCallback(\n (result: ArtifactSaveResult, content: string) => {\n // A host that keeps passing the local draft's placeholder id: follow every save to the real\n // document it created (the first save creates version 1, later ones chain from it).\n if (!isDocumentId(hostDocId) && result.docId)\n setSaved({ forId: hostDocId, id: result.docId, text: content })\n onSaved?.(result, content)\n },\n [hostDocId, onSaved]\n )\n\n const session = useArtifactEditSession({\n ...passthrough,\n docId,\n // A local draft has only its own text; a just-saved one shows what was saved until the parsed\n // text of the new document arrives.\n baseline: text ?? (isLocal ? draftContent : adopted?.text),\n savedFromId: current?.savedFromId,\n compareId: activeCompareId,\n onCompareChange: changeCompare,\n compareText,\n compareLoading,\n save,\n onSaved: handleSaved,\n })\n\n const { redline } = session\n return useMemo<ArtifactSession>(\n () => ({\n ...session,\n docId,\n isLocal,\n isLoading,\n status,\n versions,\n labelOf: (id) => versionLabel(versions, id),\n view: {\n content: session.baseline,\n isEditing: session.isEditing,\n hasChanges: session.hasChanges,\n isSaving: session.isSaving,\n canCompare: session.canCompare,\n comparing: !!session.compareId,\n compareId: session.compareId,\n showTrackChanges: session.showTrackChanges,\n versions,\n currentVersionId: current?.id ?? docId ?? '',\n onStartEdit: session.startEdit,\n onCancel: session.cancel,\n onSave: () => void session.save(),\n onCompare: () => session.compare(),\n onStopCompare: session.stopCompare,\n onToggleTrackChanges: () => session.setShowTrackChanges(!session.showTrackChanges),\n // A local draft has its text already: it is ready.\n status: isLocal ? 'ready' : status,\n onRetry: retry,\n },\n editorProps: {\n markdown: session.content,\n onChange: session.change,\n onNormalized: session.normalized,\n },\n redlineProps: {\n state: redline.state,\n onRevert: (regionId) => void session.revertRegion(regionId),\n },\n }),\n [session, redline, isLoading, status, retry, isLocal, versions, docId]\n )\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { registerDocumentTurnFlush } from '../chat/turn-flush'\nimport { useCallback, useEffect, useMemo, useState } from 'react'\nimport { useMutation, useQueryClient } from '@tanstack/react-query'\nimport { useArbi } from '../useArbi'\nimport { arbiQueryKeys, useDocumentVersionList } from '../hooks'\nimport { refreshDocuments, useDocuments } from '../documents/documents-data'\nimport { useArtifactText, type ArtifactTextStatus } from './useArtifactText'\nimport {\n documentVersions,\n findVersion,\n isDocumentId,\n parseVersionRef,\n toArtifactVersions,\n versionLabel,\n type ArtifactVersion,\n} from './versions'\nimport {\n useArtifactEditSession,\n type ArtifactEditSession,\n type ArtifactEditSessionOptions,\n type ArtifactSaveResult,\n} from './useArtifactEditSession'\n\n/**\n * The versions of an artifact, oldest first. `docId` is a document id or a version reference\n * (`doc-\u2026@v2`). Every listed document carries its versions; a document that is not in the\n * workspace list asks for its version list.\n */\nexport function useArtifactVersions(\n workspaceId: string | undefined,\n docId: string | null | undefined\n): ArtifactVersion[] {\n const plainId = docId ? parseVersionRef(docId).docId : null\n const { data: documents } = useDocuments(workspaceId)\n const listed = useMemo(\n () => (documents ?? []).find((d) => d.external_id === plainId),\n [documents, plainId]\n )\n // Asked only once the list has loaded and the document is not in it.\n const { data: history } = useDocumentVersionList(plainId, {\n enabled: !!plainId && documents !== undefined && !listed,\n })\n return useMemo(\n () => (listed ? documentVersions(listed) : toArtifactVersions(plainId, history)),\n [listed, history, plainId]\n )\n}\n\nexport interface SaveArtifactVersionInput {\n /** File name of the new version (`.md` is added when missing). */\n title: string\n content: string\n /** Save as the next version of this document (it keeps its id); omit for a new document. */\n autosave?: boolean\n versionOf?: string | null\n /** The processing config (auto-title, parsing) to run the upload with. */\n configId?: string | null\n /** `artifact` (default) for AI-style drafts; `source` for a document the user typed. */\n wpType?: 'artifact' | 'source'\n}\n\n/**\n * Save markdown as a NEW immutable version (the next version of `versionOf`'s document, which keeps\n * its id) and refresh the workspace document list, version lists and the document's text.\n */\nexport function useSaveArtifactVersion() {\n const arbi = useArbi()\n const queryClient = useQueryClient()\n return useMutation<ArtifactSaveResult, Error, SaveArtifactVersionInput>({\n mutationFn: async ({ title, content, versionOf, configId, wpType, autosave }) => {\n // The backend classifies uploads by extension: a title without one is silently dropped.\n const fileName = /\\.md$/i.test(title) ? title : `${title}.md`\n const result = await arbi.documents.uploadFile(\n new Blob([content], { type: 'text/markdown' }),\n fileName,\n {\n wpType: wpType ?? 'artifact',\n autosave,\n versionOf: versionOf ? parseVersionRef(versionOf).docId : undefined,\n configExtId: configId ?? undefined,\n }\n )\n const docId = result.doc_ext_ids?.[0]\n // The document's row moves to the new version (its version lists re-read with it).\n if (docId) await refreshDocuments(queryClient, arbi, [docId])\n await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.allDocumentVersions() })\n // The document's current text is the new version's now.\n if (docId)\n await queryClient.invalidateQueries({ queryKey: arbiQueryKeys.parsedDocument(docId) })\n return { docId, version: result.version ?? undefined }\n },\n })\n}\n\nexport interface ArtifactSessionOptions extends Pick<\n ArtifactEditSessionOptions,\n | 'onSaved'\n | 'onSaveError'\n | 'onCompareChange'\n | 'compareId'\n | 'compareOpensEditor'\n | 'redlineEngine'\n | 'onDirtyChange'\n> {\n workspaceId: string | undefined\n /**\n * The open document (its current version), a version reference `doc-\u2026@v{n}`, or the placeholder\n * id of an unsaved local draft (anything that is not a backend document id, i.e. does not start\n * with `doc`).\n */\n docId: string | null | undefined\n /**\n * The text of a local draft (nothing is fetched for it). The FIRST save uploads it as a real\n * document with no parent and the session switches to the new id; later saves chain from it.\n */\n draftContent?: string\n /** Name for saved versions. Defaults to the open version's file name, else \"Untitled\". */\n title?: string\n /** Processing config for saved versions. */\n configId?: string | null\n}\n\n/** Everything `ArtifactEditor` (`@arbidocs/blocks/artifacts`) takes from a session, flat. */\nexport interface ArtifactSessionView {\n content: string\n isEditing: boolean\n hasChanges: boolean\n isSaving: boolean\n canCompare: boolean\n comparing: boolean\n compareId: string | null\n showTrackChanges: boolean\n versions: ArtifactVersion[]\n currentVersionId: string\n onStartEdit: () => void\n onCancel: () => void\n onSave: () => void\n onCompare: () => void\n onStopCompare: () => void\n onToggleTrackChanges: () => void\n /**\n * Whether the text is ready. `ArtifactEditor` renders \"Preparing editor...\" while it is\n * `loading` or `parsing` (a fresh upload is parsed for a few seconds) and an error with Retry.\n */\n status: ArtifactTextStatus\n /** Ask for the text again after an `error`. */\n onRetry: () => void\n}\n\nexport interface ArtifactSession extends ArtifactEditSession {\n /** The open document: the new real id once a local draft has been saved. */\n docId: string | null | undefined\n /** The open version is an unsaved local draft (no backend document yet). */\n isLocal: boolean\n isLoading: boolean\n /** See `ArtifactTextStatus`. */\n status: ArtifactTextStatus\n versions: ArtifactVersion[]\n /** \"v2\" for any version (reference) of the document. */\n labelOf: (id: string | null | undefined) => string | null\n /** Props for `<ArtifactEditor>`. */\n view: ArtifactSessionView\n /** Props for `<ArtifactMarkdownEditor>` (`@arbidocs/blocks/artifacts/editor`). */\n editorProps: {\n markdown: string\n onChange: (markdown: string) => void\n onNormalized: (markdown: string) => void\n }\n /** Props for `<ArtifactRedlineView>`. */\n redlineProps: {\n state: ArtifactEditSession['redline']['state']\n onRevert: (regionId: string) => void\n }\n}\n\n/**\n * Edit an artifact through the SDK: load it, edit it, save it as a new version, list its versions\n * and compare any two with track changes. Needs an `<ArbiHost>` with an `arbi` and a selected workspace.\n *\n * ```tsx\n * const session = useArtifactSession({ workspaceId, docId, onSaved: ({ docId }) => docId && open(docId) })\n * <ArtifactEditor {...session.view} onOpenVersion={open}\n * editor={<ArtifactMarkdownEditor {...session.editorProps} />}\n * redline={<ArtifactRedlineView {...session.redlineProps} />} />\n * ```\n */\nexport function useArtifactSession(options: ArtifactSessionOptions): ArtifactSession {\n const {\n workspaceId,\n docId: hostDocId,\n draftContent,\n title,\n configId,\n compareId,\n onCompareChange,\n onSaved,\n ...passthrough\n } = options\n // A saved local draft becomes this real document; another host docId resets it.\n const [saved, setSaved] = useState<{\n forId: string | null | undefined\n id: string\n text: string\n } | null>(null)\n const adopted = saved && saved.forId === hostDocId ? saved : null\n const docId = adopted?.id ?? hostDocId\n const isLocal = !isDocumentId(docId)\n\n // Owned here (not in the edit session) because the compare target's text must be loaded first.\n const [ownCompareId, setOwnCompareId] = useState<string | null>(null)\n const activeCompareId = compareId === undefined ? ownCompareId : compareId\n const changeCompare = useCallback(\n (id: string | null) => {\n setOwnCompareId(id)\n onCompareChange?.(id)\n },\n [onCompareChange]\n )\n\n const { text, isLoading, status, retry } = useArtifactText(docId)\n const versions = useArtifactVersions(workspaceId, isLocal ? null : docId)\n const { text: compareText, isLoading: compareLoading } = useArtifactText(activeCompareId, {\n enabled: !!activeCompareId,\n })\n const saveVersion = useSaveArtifactVersion()\n const autosaveVersion = saveVersion.mutateAsync\n const current = findVersion(versions, docId)\n\n const save = useCallback(\n async (content: string) => {\n const result = await saveVersion.mutateAsync({\n title: title ?? current?.name ?? 'Untitled',\n content,\n versionOf: isLocal ? null : docId,\n configId,\n })\n const latest = versions.reduce((n, v) => Math.max(n, v.number), 0)\n return { ...result, version: result.version ?? (isLocal ? 1 : latest + 1) }\n },\n [saveVersion, title, current?.name, isLocal, docId, configId, versions]\n )\n\n const handleSaved = useCallback(\n (result: ArtifactSaveResult, content: string) => {\n // A host that keeps passing the local draft's placeholder id: follow every save to the real\n // document it created (the first save creates version 1, later ones chain from it).\n if (!isDocumentId(hostDocId) && result.docId)\n setSaved({ forId: hostDocId, id: result.docId, text: content })\n onSaved?.(result, content)\n },\n [hostDocId, onSaved]\n )\n\n const session = useArtifactEditSession({\n ...passthrough,\n docId,\n // A local draft has only its own text; a just-saved one shows what was saved until the parsed\n // text of the new document arrives.\n baseline: text ?? (isLocal ? draftContent : adopted?.text),\n savedFromId: current?.savedFromId,\n compareId: activeCompareId,\n onCompareChange: changeCompare,\n compareText,\n compareLoading,\n save,\n onSaved: handleSaved,\n })\n\n useEffect(() => {\n if (isLocal || !session.hasChanges || session.isSaving) return\n const timer = setTimeout(() => {\n void autosaveVersion({\n title: title ?? current?.name ?? 'Untitled',\n content: session.content,\n versionOf: docId,\n configId,\n autosave: true,\n }).catch(() => undefined)\n }, 2500)\n return () => clearTimeout(timer)\n }, [\n isLocal,\n session.content,\n session.hasChanges,\n session.isSaving,\n docId,\n title,\n current?.name,\n configId,\n autosaveVersion,\n ])\n\n useEffect(() => {\n if (!docId) return\n return registerDocumentTurnFlush(docId, async () => {\n if (session.hasChanges && !(await session.save()))\n throw new Error('Save the document before sending your message.')\n })\n }, [docId, session])\n\n const { redline } = session\n return useMemo<ArtifactSession>(\n () => ({\n ...session,\n docId,\n isLocal,\n isLoading,\n status,\n versions,\n labelOf: (id) => versionLabel(versions, id),\n view: {\n content: session.baseline,\n isEditing: session.isEditing,\n hasChanges: session.hasChanges || !!current?.autosave,\n isSaving: session.isSaving,\n canCompare: session.canCompare,\n comparing: !!session.compareId,\n compareId: session.compareId,\n showTrackChanges: session.showTrackChanges,\n versions,\n currentVersionId: current?.id ?? docId ?? '',\n onStartEdit: session.startEdit,\n onCancel: session.cancel,\n onSave: () => void session.save(),\n onCompare: () => session.compare(),\n onStopCompare: session.stopCompare,\n onToggleTrackChanges: () => session.setShowTrackChanges(!session.showTrackChanges),\n // A local draft has its text already: it is ready.\n status: isLocal ? 'ready' : status,\n onRetry: retry,\n },\n editorProps: {\n markdown: session.content,\n onChange: session.change,\n onNormalized: session.normalized,\n },\n redlineProps: {\n state: redline.state,\n onRevert: (regionId) => void session.revertRegion(regionId),\n },\n }),\n [\n session,\n redline,\n isLoading,\n status,\n retry,\n isLocal,\n versions,\n docId,\n current?.id,\n current?.autosave,\n ]\n )\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -22,6 +22,7 @@ function toArtifactVersions(docId, entries, currentVersion) {
22
22
  name: e.file_name ?? "",
23
23
  createdAt: e.created_at ?? "",
24
24
  number: e.n,
25
+ autosave: e.autosave,
25
26
  reason: e.reason ?? "upload",
26
27
  isCurrent: current != null ? e.n === current : !!e.is_current,
27
28
  savedFromId: savedFromId(docId, e),
@@ -41,7 +42,7 @@ function findVersion(versions, ref) {
41
42
  function versionLabel(versions, ref) {
42
43
  if (!ref || versions.length <= 1) return null;
43
44
  const version = findVersion(versions, ref);
44
- return version ? `v${version.number}` : null;
45
+ return version ? `v${version.number}${version.autosave ? " \xB7 Autosaved" : ""}` : null;
45
46
  }
46
47
  function flattenChunks(chunks, options) {
47
48
  const text = (chunks ?? []).map((c) => (c.content ?? "").trim()).join("\n\n");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/artifacts/versions.ts"],
4
- "sourcesContent": ["/**\n * Versions of a document. A document keeps ONE id for its whole life; its versions live inside it\n * (`current_version` + `versions`, numbered 1..n). A version is named by a version reference\n * `doc-\u2026@v{n}`; a plain document id means its current version. The workspace list carries every\n * document's `versions`, and `GET /v1/document/{id}/versions` (`arbi.documents.versions`) the same\n * list on its own.\n */\n\nimport { repairParsedStructure, repairParsedTitle, unescapeMarkdown } from '../ai/stored-markdown'\nimport { normalizeMarkdown } from './normalize-markdown'\n\n/** One version entry as the API returns it (a subset of `DocVersion`). */\nexport interface DocVersionEntry {\n n: number\n file_name?: string | null\n created_at?: string | null\n reason?: string | null\n /** The version an edit started from, when it was not the previous one. */\n made_from?: number | null\n is_current?: boolean | null\n /** The assistant message whose turn made this version, and its thread (none: a person's). */\n source_message_ext_id?: string | null\n conversation_ext_id?: string | null\n}\n\n/** The fields of a workspace document the version list needs (a subset of the API type). */\nexport interface VersionedDoc {\n external_id?: string | null\n current_version?: number | null\n versions?: readonly DocVersionEntry[] | null\n file_name?: string | null\n created_at?: string | null\n}\n\n/** One row of a version list. Matches `VersionEntry` of `@arbidocs/blocks/artifacts`. */\nexport interface ArtifactVersion {\n /** The version reference `doc-\u2026@v{n}`: what opening or comparing this version names. */\n id: string\n /** The document (the same for every version). */\n docId: string\n /** File name as stored. */\n name: string\n /** ISO timestamp. */\n createdAt: string\n /** 1-based version number within the document. */\n number: number\n /** upload | edit | signed | sync */\n reason: string\n isCurrent: boolean\n /** The version this one was saved from (an older one than the previous version when restored). */\n savedFromId?: string | null\n /** The thread (and its message) whose turn made this version; none for a person's. */\n thread?: { conversationId: string; messageId: string | null } | null\n}\n\nconst VERSION_REF = /^(doc-[A-Za-z0-9]+)@v([1-9][0-9]*)$/\n\n/** `doc-\u2026@v{n}`: a reference to version `n` of a document. */\nexport function versionRef(docId: string, version: number): string {\n return `${docId}@v${version}`\n}\n\n/**\n * Split a reference into the document and the version it names. A plain document id (or anything\n * else) names no version: the document's current one.\n */\nexport function parseVersionRef(ref: string | null | undefined): {\n docId: string\n version: number | null\n} {\n const match = VERSION_REF.exec(ref ?? '')\n if (!match) return { docId: ref ?? '', version: null }\n return { docId: match[1], version: Number(match[2]) }\n}\n\n/** The version a version was saved from: `made_from`, else the one before it. */\nexport function savedFromId(docId: string, entry: DocVersionEntry): string | null {\n if (entry.made_from) return versionRef(docId, entry.made_from)\n return entry.n > 1 ? versionRef(docId, entry.n - 1) : null\n}\n\n/** A document's version entries (oldest first) as plain version rows. */\nexport function toArtifactVersions(\n docId: string | null | undefined,\n entries: readonly DocVersionEntry[] | null | undefined,\n currentVersion?: number | null\n): ArtifactVersion[] {\n if (!docId) return []\n const current = currentVersion ?? entries?.find((e) => e.is_current)?.n ?? null\n return [...(entries ?? [])]\n .sort((a, b) => a.n - b.n)\n .map((e) => ({\n id: versionRef(docId, e.n),\n docId,\n name: e.file_name ?? '',\n createdAt: e.created_at ?? '',\n number: e.n,\n reason: e.reason ?? 'upload',\n isCurrent: current != null ? e.n === current : !!e.is_current,\n savedFromId: savedFromId(docId, e),\n thread: e.conversation_ext_id\n ? { conversationId: e.conversation_ext_id, messageId: e.source_message_ext_id ?? null }\n : null,\n }))\n}\n\n/** The versions of a listed document, oldest first (`[]` when the document is not given). */\nexport function documentVersions(doc: VersionedDoc | null | undefined): ArtifactVersion[] {\n if (!doc?.external_id) return []\n const entries = doc.versions?.length\n ? doc.versions\n : [{ n: doc.current_version ?? 1, file_name: doc.file_name, created_at: doc.created_at }]\n return toArtifactVersions(doc.external_id, entries, doc.current_version ?? null)\n}\n\n/** The row `ref` names: an exact version, or (a plain document id) the current version. */\nexport function findVersion(\n versions: readonly ArtifactVersion[],\n ref: string | null | undefined\n): ArtifactVersion | undefined {\n if (!ref) return undefined\n const { docId, version } = parseVersionRef(ref)\n return version == null\n ? versions.find((v) => v.docId === docId && v.isCurrent)\n : versions.find((v) => v.docId === docId && v.number === version)\n}\n\n/**\n * \"v2\": the version number `ref` names. `null` when there is nothing to tell apart (the document\n * has a single version, or it is unknown), so unversioned documents stay unlabelled.\n */\nexport function versionLabel(\n versions: readonly ArtifactVersion[],\n ref: string | null | undefined\n): string | null {\n if (!ref || versions.length <= 1) return null\n const version = findVersion(versions, ref)\n return version ? `v${version.number}` : null\n}\n\n/**\n * Join parsed chunks into one markdown text (blank line between chunks).\n *\n * `unescape: true` for an artifact stored from markdown: the backend's parse of a `.md` escapes\n * markdown punctuation, which `unescapeMarkdown` undoes (see its rule), and mangles its title\n * (`repairParsedTitle`, `repairParsedStructure`: doubled headings, `- ` stacked on list markers,\n * one-line tables), then normalised. Leave it off for the parse\n * of a PDF or Word file.\n */\nexport function flattenChunks(\n chunks: readonly { content?: string | null }[] | null | undefined,\n options?: { unescape?: boolean }\n): string {\n const text = (chunks ?? []).map((c) => (c.content ?? '').trim()).join('\\n\\n')\n if (!options?.unescape) return text\n // Canonical spelling last, so the text an editor starts from is what a save would persist.\n return normalizeMarkdown(\n repairParsedStructure(repairParsedTitle(unescapeMarkdown(text)))\n ).replace(/\\n$/, '')\n}\n\n/** The backend's pattern for a document id. Anything else (a streamed draft's placeholder) is local. */\nconst DOC_ID = /^doc/\n\n/** Is `id` a real, saved document id? Local drafts have placeholder ids and no backend document. */\nexport function isDocumentId(id: string | null | undefined): id is string {\n return !!id && DOC_ID.test(id)\n}\n"],
5
- "mappings": "AAQA,SAAS,uBAAuB,mBAAmB,wBAAwB;AAC3E,SAAS,yBAAyB;AA8ClC,MAAM,cAAc;AAGb,SAAS,WAAW,OAAe,SAAyB;AACjE,SAAO,GAAG,KAAK,KAAK,OAAO;AAC7B;AAMO,SAAS,gBAAgB,KAG9B;AACA,QAAM,QAAQ,YAAY,KAAK,OAAO,EAAE;AACxC,MAAI,CAAC,MAAO,QAAO,EAAE,OAAO,OAAO,IAAI,SAAS,KAAK;AACrD,SAAO,EAAE,OAAO,MAAM,CAAC,GAAG,SAAS,OAAO,MAAM,CAAC,CAAC,EAAE;AACtD;AAGO,SAAS,YAAY,OAAe,OAAuC;AAChF,MAAI,MAAM,UAAW,QAAO,WAAW,OAAO,MAAM,SAAS;AAC7D,SAAO,MAAM,IAAI,IAAI,WAAW,OAAO,MAAM,IAAI,CAAC,IAAI;AACxD;AAGO,SAAS,mBACd,OACA,SACA,gBACmB;AACnB,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAM,UAAU,kBAAkB,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK;AAC3E,SAAO,CAAC,GAAI,WAAW,CAAC,CAAE,EACvB,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,EACxB,IAAI,CAAC,OAAO;AAAA,IACX,IAAI,WAAW,OAAO,EAAE,CAAC;AAAA,IACzB;AAAA,IACA,MAAM,EAAE,aAAa;AAAA,IACrB,WAAW,EAAE,cAAc;AAAA,IAC3B,QAAQ,EAAE;AAAA,IACV,QAAQ,EAAE,UAAU;AAAA,IACpB,WAAW,WAAW,OAAO,EAAE,MAAM,UAAU,CAAC,CAAC,EAAE;AAAA,IACnD,aAAa,YAAY,OAAO,CAAC;AAAA,IACjC,QAAQ,EAAE,sBACN,EAAE,gBAAgB,EAAE,qBAAqB,WAAW,EAAE,yBAAyB,KAAK,IACpF;AAAA,EACN,EAAE;AACN;AAGO,SAAS,iBAAiB,KAAyD;AACxF,MAAI,CAAC,KAAK,YAAa,QAAO,CAAC;AAC/B,QAAM,UAAU,IAAI,UAAU,SAC1B,IAAI,WACJ,CAAC,EAAE,GAAG,IAAI,mBAAmB,GAAG,WAAW,IAAI,WAAW,YAAY,IAAI,WAAW,CAAC;AAC1F,SAAO,mBAAmB,IAAI,aAAa,SAAS,IAAI,mBAAmB,IAAI;AACjF;AAGO,SAAS,YACd,UACA,KAC6B;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,EAAE,OAAO,QAAQ,IAAI,gBAAgB,GAAG;AAC9C,SAAO,WAAW,OACd,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,EAAE,SAAS,IACrD,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,EAAE,WAAW,OAAO;AACpE;AAMO,SAAS,aACd,UACA,KACe;AACf,MAAI,CAAC,OAAO,SAAS,UAAU,EAAG,QAAO;AACzC,QAAM,UAAU,YAAY,UAAU,GAAG;AACzC,SAAO,UAAU,IAAI,QAAQ,MAAM,KAAK;AAC1C;AAWO,SAAS,cACd,QACA,SACQ;AACR,QAAM,QAAQ,UAAU,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,KAAK,CAAC,EAAE,KAAK,MAAM;AAC5E,MAAI,CAAC,SAAS,SAAU,QAAO;AAE/B,SAAO;AAAA,IACL,sBAAsB,kBAAkB,iBAAiB,IAAI,CAAC,CAAC;AAAA,EACjE,EAAE,QAAQ,OAAO,EAAE;AACrB;AAGA,MAAM,SAAS;AAGR,SAAS,aAAa,IAA6C;AACxE,SAAO,CAAC,CAAC,MAAM,OAAO,KAAK,EAAE;AAC/B;",
4
+ "sourcesContent": ["/**\n * Versions of a document. A document keeps ONE id for its whole life; its versions live inside it\n * (`current_version` + `versions`, numbered 1..n). A version is named by a version reference\n * `doc-\u2026@v{n}`; a plain document id means its current version. The workspace list carries every\n * document's `versions`, and `GET /v1/document/{id}/versions` (`arbi.documents.versions`) the same\n * list on its own.\n */\n\nimport { repairParsedStructure, repairParsedTitle, unescapeMarkdown } from '../ai/stored-markdown'\nimport { normalizeMarkdown } from './normalize-markdown'\n\n/** One version entry as the API returns it (a subset of `DocVersion`). */\nexport interface DocVersionEntry {\n autosave?: boolean\n n: number\n file_name?: string | null\n created_at?: string | null\n reason?: string | null\n /** The version an edit started from, when it was not the previous one. */\n made_from?: number | null\n is_current?: boolean | null\n /** The assistant message whose turn made this version, and its thread (none: a person's). */\n source_message_ext_id?: string | null\n conversation_ext_id?: string | null\n}\n\n/** The fields of a workspace document the version list needs (a subset of the API type). */\nexport interface VersionedDoc {\n external_id?: string | null\n current_version?: number | null\n versions?: readonly DocVersionEntry[] | null\n file_name?: string | null\n created_at?: string | null\n}\n\n/** One row of a version list. Matches `VersionEntry` of `@arbidocs/blocks/artifacts`. */\nexport interface ArtifactVersion {\n /** The version reference `doc-\u2026@v{n}`: what opening or comparing this version names. */\n id: string\n /** The document (the same for every version). */\n docId: string\n /** File name as stored. */\n name: string\n /** ISO timestamp. */\n createdAt: string\n /** 1-based version number within the document. */\n number: number\n /** upload | edit | signed | sync */\n reason: string\n isCurrent: boolean\n /** The version this one was saved from (an older one than the previous version when restored). */\n autosave?: boolean\n savedFromId?: string | null\n /** The thread (and its message) whose turn made this version; none for a person's. */\n thread?: { conversationId: string; messageId: string | null } | null\n}\n\nconst VERSION_REF = /^(doc-[A-Za-z0-9]+)@v([1-9][0-9]*)$/\n\n/** `doc-\u2026@v{n}`: a reference to version `n` of a document. */\nexport function versionRef(docId: string, version: number): string {\n return `${docId}@v${version}`\n}\n\n/**\n * Split a reference into the document and the version it names. A plain document id (or anything\n * else) names no version: the document's current one.\n */\nexport function parseVersionRef(ref: string | null | undefined): {\n docId: string\n version: number | null\n} {\n const match = VERSION_REF.exec(ref ?? '')\n if (!match) return { docId: ref ?? '', version: null }\n return { docId: match[1], version: Number(match[2]) }\n}\n\n/** The version a version was saved from: `made_from`, else the one before it. */\nexport function savedFromId(docId: string, entry: DocVersionEntry): string | null {\n if (entry.made_from) return versionRef(docId, entry.made_from)\n return entry.n > 1 ? versionRef(docId, entry.n - 1) : null\n}\n\n/** A document's version entries (oldest first) as plain version rows. */\nexport function toArtifactVersions(\n docId: string | null | undefined,\n entries: readonly DocVersionEntry[] | null | undefined,\n currentVersion?: number | null\n): ArtifactVersion[] {\n if (!docId) return []\n const current = currentVersion ?? entries?.find((e) => e.is_current)?.n ?? null\n return [...(entries ?? [])]\n .sort((a, b) => a.n - b.n)\n .map((e) => ({\n id: versionRef(docId, e.n),\n docId,\n name: e.file_name ?? '',\n createdAt: e.created_at ?? '',\n number: e.n,\n autosave: e.autosave,\n reason: e.reason ?? 'upload',\n isCurrent: current != null ? e.n === current : !!e.is_current,\n savedFromId: savedFromId(docId, e),\n thread: e.conversation_ext_id\n ? { conversationId: e.conversation_ext_id, messageId: e.source_message_ext_id ?? null }\n : null,\n }))\n}\n\n/** The versions of a listed document, oldest first (`[]` when the document is not given). */\nexport function documentVersions(doc: VersionedDoc | null | undefined): ArtifactVersion[] {\n if (!doc?.external_id) return []\n const entries = doc.versions?.length\n ? doc.versions\n : [{ n: doc.current_version ?? 1, file_name: doc.file_name, created_at: doc.created_at }]\n return toArtifactVersions(doc.external_id, entries, doc.current_version ?? null)\n}\n\n/** The row `ref` names: an exact version, or (a plain document id) the current version. */\nexport function findVersion(\n versions: readonly ArtifactVersion[],\n ref: string | null | undefined\n): ArtifactVersion | undefined {\n if (!ref) return undefined\n const { docId, version } = parseVersionRef(ref)\n return version == null\n ? versions.find((v) => v.docId === docId && v.isCurrent)\n : versions.find((v) => v.docId === docId && v.number === version)\n}\n\n/**\n * \"v2\": the version number `ref` names. `null` when there is nothing to tell apart (the document\n * has a single version, or it is unknown), so unversioned documents stay unlabelled.\n */\nexport function versionLabel(\n versions: readonly ArtifactVersion[],\n ref: string | null | undefined\n): string | null {\n if (!ref || versions.length <= 1) return null\n const version = findVersion(versions, ref)\n return version ? `v${version.number}${version.autosave ? ' \u00B7 Autosaved' : ''}` : null\n}\n\n/**\n * Join parsed chunks into one markdown text (blank line between chunks).\n *\n * `unescape: true` for an artifact stored from markdown: the backend's parse of a `.md` escapes\n * markdown punctuation, which `unescapeMarkdown` undoes (see its rule), and mangles its title\n * (`repairParsedTitle`, `repairParsedStructure`: doubled headings, `- ` stacked on list markers,\n * one-line tables), then normalised. Leave it off for the parse\n * of a PDF or Word file.\n */\nexport function flattenChunks(\n chunks: readonly { content?: string | null }[] | null | undefined,\n options?: { unescape?: boolean }\n): string {\n const text = (chunks ?? []).map((c) => (c.content ?? '').trim()).join('\\n\\n')\n if (!options?.unescape) return text\n // Canonical spelling last, so the text an editor starts from is what a save would persist.\n return normalizeMarkdown(\n repairParsedStructure(repairParsedTitle(unescapeMarkdown(text)))\n ).replace(/\\n$/, '')\n}\n\n/** The backend's pattern for a document id. Anything else (a streamed draft's placeholder) is local. */\nconst DOC_ID = /^doc/\n\n/** Is `id` a real, saved document id? Local drafts have placeholder ids and no backend document. */\nexport function isDocumentId(id: string | null | undefined): id is string {\n return !!id && DOC_ID.test(id)\n}\n"],
5
+ "mappings": "AAQA,SAAS,uBAAuB,mBAAmB,wBAAwB;AAC3E,SAAS,yBAAyB;AAgDlC,MAAM,cAAc;AAGb,SAAS,WAAW,OAAe,SAAyB;AACjE,SAAO,GAAG,KAAK,KAAK,OAAO;AAC7B;AAMO,SAAS,gBAAgB,KAG9B;AACA,QAAM,QAAQ,YAAY,KAAK,OAAO,EAAE;AACxC,MAAI,CAAC,MAAO,QAAO,EAAE,OAAO,OAAO,IAAI,SAAS,KAAK;AACrD,SAAO,EAAE,OAAO,MAAM,CAAC,GAAG,SAAS,OAAO,MAAM,CAAC,CAAC,EAAE;AACtD;AAGO,SAAS,YAAY,OAAe,OAAuC;AAChF,MAAI,MAAM,UAAW,QAAO,WAAW,OAAO,MAAM,SAAS;AAC7D,SAAO,MAAM,IAAI,IAAI,WAAW,OAAO,MAAM,IAAI,CAAC,IAAI;AACxD;AAGO,SAAS,mBACd,OACA,SACA,gBACmB;AACnB,MAAI,CAAC,MAAO,QAAO,CAAC;AACpB,QAAM,UAAU,kBAAkB,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,KAAK;AAC3E,SAAO,CAAC,GAAI,WAAW,CAAC,CAAE,EACvB,KAAK,CAAC,GAAG,MAAM,EAAE,IAAI,EAAE,CAAC,EACxB,IAAI,CAAC,OAAO;AAAA,IACX,IAAI,WAAW,OAAO,EAAE,CAAC;AAAA,IACzB;AAAA,IACA,MAAM,EAAE,aAAa;AAAA,IACrB,WAAW,EAAE,cAAc;AAAA,IAC3B,QAAQ,EAAE;AAAA,IACV,UAAU,EAAE;AAAA,IACZ,QAAQ,EAAE,UAAU;AAAA,IACpB,WAAW,WAAW,OAAO,EAAE,MAAM,UAAU,CAAC,CAAC,EAAE;AAAA,IACnD,aAAa,YAAY,OAAO,CAAC;AAAA,IACjC,QAAQ,EAAE,sBACN,EAAE,gBAAgB,EAAE,qBAAqB,WAAW,EAAE,yBAAyB,KAAK,IACpF;AAAA,EACN,EAAE;AACN;AAGO,SAAS,iBAAiB,KAAyD;AACxF,MAAI,CAAC,KAAK,YAAa,QAAO,CAAC;AAC/B,QAAM,UAAU,IAAI,UAAU,SAC1B,IAAI,WACJ,CAAC,EAAE,GAAG,IAAI,mBAAmB,GAAG,WAAW,IAAI,WAAW,YAAY,IAAI,WAAW,CAAC;AAC1F,SAAO,mBAAmB,IAAI,aAAa,SAAS,IAAI,mBAAmB,IAAI;AACjF;AAGO,SAAS,YACd,UACA,KAC6B;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,QAAM,EAAE,OAAO,QAAQ,IAAI,gBAAgB,GAAG;AAC9C,SAAO,WAAW,OACd,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,EAAE,SAAS,IACrD,SAAS,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,EAAE,WAAW,OAAO;AACpE;AAMO,SAAS,aACd,UACA,KACe;AACf,MAAI,CAAC,OAAO,SAAS,UAAU,EAAG,QAAO;AACzC,QAAM,UAAU,YAAY,UAAU,GAAG;AACzC,SAAO,UAAU,IAAI,QAAQ,MAAM,GAAG,QAAQ,WAAW,oBAAiB,EAAE,KAAK;AACnF;AAWO,SAAS,cACd,QACA,SACQ;AACR,QAAM,QAAQ,UAAU,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,WAAW,IAAI,KAAK,CAAC,EAAE,KAAK,MAAM;AAC5E,MAAI,CAAC,SAAS,SAAU,QAAO;AAE/B,SAAO;AAAA,IACL,sBAAsB,kBAAkB,iBAAiB,IAAI,CAAC,CAAC;AAAA,EACjE,EAAE,QAAQ,OAAO,EAAE;AACrB;AAGA,MAAM,SAAS;AAGR,SAAS,aAAa,IAA6C;AACxE,SAAO,CAAC,CAAC,MAAM,OAAO,KAAK,EAAE;AAC/B;",
6
6
  "names": []
7
7
  }
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- var chunkFAJLJDWS_cjs = require('../../chunk-FAJLJDWS.cjs');
4
- require('../../chunk-7L7QRIQ2.cjs');
3
+ var chunkFOBB5U4Y_cjs = require('../../chunk-FOBB5U4Y.cjs');
4
+ require('../../chunk-H3XHBIZ6.cjs');
5
5
  var chunkO32T7XRD_cjs = require('../../chunk-O32T7XRD.cjs');
6
6
  var chunkIX4HOS5Z_cjs = require('../../chunk-IX4HOS5Z.cjs');
7
7
  require('../../chunk-N73IMVFF.cjs');
8
- require('../../chunk-NDIKMOBJ.cjs');
8
+ require('../../chunk-KULS4YWP.cjs');
9
9
  require('../../chunk-VOFNX4P6.cjs');
10
10
  require('../../chunk-EEIYXVM4.cjs');
11
11
  require('../../chunk-5NOZDH3F.cjs');
@@ -123,8 +123,8 @@ function useCallTranscriber({
123
123
  const arbi = chunkO32T7XRD_cjs.useArbi();
124
124
  const queryClient = reactQuery.useQueryClient();
125
125
  const profile = arbi.isLoggedIn ? arbi.user.profile() : null;
126
- const myProfileName = profile && chunkFAJLJDWS_cjs.toProfileView(profile).fullName || null;
127
- const deploymentAvailable = chunkFAJLJDWS_cjs.useCallTranscriptionAvailable();
126
+ const myProfileName = profile && chunkFOBB5U4Y_cjs.toProfileView(profile).fullName || null;
127
+ const deploymentAvailable = chunkFOBB5U4Y_cjs.useCallTranscriptionAvailable();
128
128
  const available = deploymentAvailable && !!callKey;
129
129
  const [state, setState] = react.useState(null);
130
130
  const [creatorExtId, setCreatorExtId] = react.useState(null);
@@ -167,7 +167,7 @@ function useCallTranscriber({
167
167
  };
168
168
  }, [available, callExtId]);
169
169
  react.useEffect(
170
- () => chunkFAJLJDWS_cjs.onCallEvent("call.transcription", (message) => {
170
+ () => chunkFOBB5U4Y_cjs.onCallEvent("call.transcription", (message) => {
171
171
  if (message.call_ext_id === callExtId) applyState(message);
172
172
  }),
173
173
  [callExtId, applyState]
@@ -183,7 +183,7 @@ function useCallTranscriber({
183
183
  const persist = react.useCallback(
184
184
  (segment, speakerNeedsProxy) => {
185
185
  if (!callKey || persisted.current.has(segment.segmentId)) return;
186
- const role = chunkFAJLJDWS_cjs.transcriptPersistRole({
186
+ const role = chunkFOBB5U4Y_cjs.transcriptPersistRole({
187
187
  myIdentity,
188
188
  canPost: canPost && (segment.speakerIdentity !== myIdentity || selfPosts.current),
189
189
  hasCallKey: true,
@@ -192,7 +192,7 @@ function useCallTranscriber({
192
192
  });
193
193
  if (!role) return;
194
194
  persisted.current.add(segment.segmentId);
195
- chunkFAJLJDWS_cjs.persistTranscriptSegment(arbi, queryClient, callExtId, callKey, segment).catch(
195
+ chunkFOBB5U4Y_cjs.persistTranscriptSegment(arbi, queryClient, callExtId, callKey, segment).catch(
196
196
  (err) => {
197
197
  persisted.current.delete(segment.segmentId);
198
198
  if (role === "own" && err?.status === 403) {
@@ -212,7 +212,7 @@ function useCallTranscriber({
212
212
  );
213
213
  react.useEffect(() => {
214
214
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
215
- if (topic === chunkFAJLJDWS_cjs.CALL_TRANSCRIPTION_TOPIC) {
215
+ if (topic === chunkFOBB5U4Y_cjs.CALL_TRANSCRIPTION_TOPIC) {
216
216
  if (participant || participantIdentity) return;
217
217
  try {
218
218
  const next = browser.calls.transcriptionStateFrom(JSON.parse(new TextDecoder().decode(payload)));
@@ -221,10 +221,10 @@ function useCallTranscriber({
221
221
  }
222
222
  return;
223
223
  }
224
- if (topic !== chunkFAJLJDWS_cjs.CALL_CAPTION_TOPIC || !callKey) return;
224
+ if (topic !== chunkFOBB5U4Y_cjs.CALL_CAPTION_TOPIC || !callKey) return;
225
225
  const identity = participant?.identity ?? participantIdentity;
226
226
  if (!identity || identity === myIdentity) return;
227
- void chunkFAJLJDWS_cjs.decodeCaptionPacket(payload, callKey).then((packet) => {
227
+ void chunkFOBB5U4Y_cjs.decodeCaptionPacket(payload, callKey).then((packet) => {
228
228
  if (!packet || packet.identity !== identity) return;
229
229
  const name = participant?.name || packet.name || identity;
230
230
  const t = Math.min(packet.t, Date.now());
@@ -264,7 +264,7 @@ function useCallTranscriber({
264
264
  })) {
265
265
  return;
266
266
  }
267
- const segmentId = chunkFAJLJDWS_cjs.newSegmentId();
267
+ const segmentId = chunkFOBB5U4Y_cjs.newSegmentId();
268
268
  const startedAt = new Date(segment.startedAt).toISOString();
269
269
  let text;
270
270
  try {
@@ -298,7 +298,7 @@ function useCallTranscriber({
298
298
  });
299
299
  try {
300
300
  await localParticipant.publishData(
301
- await chunkFAJLJDWS_cjs.encodeCaptionPacket(
301
+ await chunkFOBB5U4Y_cjs.encodeCaptionPacket(
302
302
  {
303
303
  v: 1,
304
304
  segment_id: segmentId,
@@ -310,7 +310,7 @@ function useCallTranscriber({
310
310
  },
311
311
  callKey
312
312
  ),
313
- { reliable: true, topic: chunkFAJLJDWS_cjs.CALL_CAPTION_TOPIC }
313
+ { reliable: true, topic: chunkFOBB5U4Y_cjs.CALL_CAPTION_TOPIC }
314
314
  );
315
315
  } catch (err) {
316
316
  console.warn("[calls] could not broadcast a caption", err);
@@ -425,14 +425,14 @@ function useCallRecorder({
425
425
  };
426
426
  }, [callExtId]);
427
427
  react.useEffect(
428
- () => chunkFAJLJDWS_cjs.onCallEvent("call.recording", (message) => {
428
+ () => chunkFOBB5U4Y_cjs.onCallEvent("call.recording", (message) => {
429
429
  if (message.call_ext_id === callExtId) applyState(message);
430
430
  }),
431
431
  [callExtId, applyState]
432
432
  );
433
433
  react.useEffect(() => {
434
434
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
435
- if (topic !== chunkFAJLJDWS_cjs.CALL_RECORDING_TOPIC) return;
435
+ if (topic !== chunkFOBB5U4Y_cjs.CALL_RECORDING_TOPIC) return;
436
436
  if (participant || participantIdentity) return;
437
437
  try {
438
438
  const next = browser.calls.recordingStateFrom(JSON.parse(new TextDecoder().decode(payload)));
@@ -453,7 +453,7 @@ function useCallRecorder({
453
453
  try {
454
454
  const row = call ?? await arbi.calls.get(callExtId);
455
455
  if (!row.workspace_locked && !row.chat_ext_id && callKey) {
456
- const ref = await chunkFAJLJDWS_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
456
+ const ref = await chunkFOBB5U4Y_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
457
457
  if (ref.kind === "chat") setCall({ ...row, chat_ext_id: ref.chatExtId });
458
458
  }
459
459
  const answer = await arbi.calls.recording.start(callExtId, mode);
@@ -555,7 +555,7 @@ function useTrackUploader(recording, callKey, kind, mimeType) {
555
555
  const queryClient = reactQuery.useQueryClient();
556
556
  return react.useMemo(() => {
557
557
  if (!shouldCapture || !recordingExtId || !call) return null;
558
- return chunkFAJLJDWS_cjs.trackUploaderFor({
558
+ return chunkFOBB5U4Y_cjs.trackUploaderFor({
559
559
  arbi,
560
560
  callExtId: call.external_id,
561
561
  recordingExtId,
@@ -563,7 +563,7 @@ function useTrackUploader(recording, callKey, kind, mimeType) {
563
563
  mimeType,
564
564
  recordingStartedAt: startedAt,
565
565
  linkVersion,
566
- conversationKey: () => chunkFAJLJDWS_cjs.conversationKeyForCall(arbi, queryClient, call, { callKey }),
566
+ conversationKey: () => chunkFOBB5U4Y_cjs.conversationKeyForCall(arbi, queryClient, call, { callKey }),
567
567
  // The server stopped the recording at the quota (its state event says so too).
568
568
  onStopped: () => markStopped()
569
569
  });
@@ -612,10 +612,10 @@ function useInCallChat({
612
612
  react.useEffect(() => {
613
613
  if (!callKey) return;
614
614
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
615
- if (topic !== chunkFAJLJDWS_cjs.CALL_CHAT_TOPIC) return;
615
+ if (topic !== chunkFOBB5U4Y_cjs.CALL_CHAT_TOPIC) return;
616
616
  const identity = participant?.identity ?? participantIdentity;
617
617
  if (!identity || identity === myIdentity) return;
618
- void chunkFAJLJDWS_cjs.decodeCallChatPacket(payload, callKey).then((packet) => {
618
+ void chunkFOBB5U4Y_cjs.decodeCallChatPacket(payload, callKey).then((packet) => {
619
619
  if (!packet) return;
620
620
  const key = `${identity}:${packet.id}`;
621
621
  if (seen.current.has(key)) return;
@@ -645,7 +645,7 @@ function useInCallChat({
645
645
  if (!open || !canRead || historyRequested.current) return;
646
646
  historyRequested.current = true;
647
647
  setHistoryLoading(true);
648
- chunkFAJLJDWS_cjs.loadMeetingHistory(arbi, queryClient, callExtId).then((result) => {
648
+ chunkFOBB5U4Y_cjs.loadMeetingHistory(arbi, queryClient, callExtId).then((result) => {
649
649
  if (!result) return;
650
650
  setMeetingChat(result.ref);
651
651
  setHistory(result.messages);
@@ -656,13 +656,13 @@ function useInCallChat({
656
656
  }, [open, canRead, callExtId]);
657
657
  const send = react.useCallback(
658
658
  async (raw) => {
659
- const text = raw.trim().slice(0, chunkFAJLJDWS_cjs.CALL_CHAT_MAX_CHARS);
659
+ const text = raw.trim().slice(0, chunkFOBB5U4Y_cjs.CALL_CHAT_MAX_CHARS);
660
660
  if (!text || !callKey || readOnly) return;
661
- const id = chunkFAJLJDWS_cjs.newPacketId();
661
+ const id = chunkFOBB5U4Y_cjs.newPacketId();
662
662
  const at = Date.now();
663
663
  await localParticipant.publishData(
664
- await chunkFAJLJDWS_cjs.encodeCallChatPacket({ v: 1, id, text, at }, callKey),
665
- { reliable: true, topic: chunkFAJLJDWS_cjs.CALL_CHAT_TOPIC }
664
+ await chunkFOBB5U4Y_cjs.encodeCallChatPacket({ v: 1, id, text, at }, callKey),
665
+ { reliable: true, topic: chunkFOBB5U4Y_cjs.CALL_CHAT_TOPIC }
666
666
  );
667
667
  setLive((prev) => [
668
668
  ...prev,
@@ -681,7 +681,7 @@ function useInCallChat({
681
681
  (prev) => prev.map((l) => l.id === id && l.senderIdentity === myIdentity ? { ...l, status } : l)
682
682
  );
683
683
  try {
684
- setMeetingChat(await chunkFAJLJDWS_cjs.persistMeetingMessage(arbi, queryClient, callExtId, callKey, text));
684
+ setMeetingChat(await chunkFOBB5U4Y_cjs.persistMeetingMessage(arbi, queryClient, callExtId, callKey, text));
685
685
  mark("saved");
686
686
  } catch (err) {
687
687
  console.warn("[calls] could not keep the message in the meeting chat", err);
@@ -693,7 +693,7 @@ function useInCallChat({
693
693
  const openMeeting = react.useCallback(async () => {
694
694
  if (!callKey) return;
695
695
  try {
696
- const ref = meetingChat ?? await chunkFAJLJDWS_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
696
+ const ref = meetingChat ?? await chunkFOBB5U4Y_cjs.ensureMeetingChat(arbi, queryClient, callExtId, callKey);
697
697
  setMeetingChat(ref);
698
698
  openMeetingChat?.(ref);
699
699
  } catch (err) {
@@ -701,7 +701,7 @@ function useInCallChat({
701
701
  }
702
702
  }, [callExtId, callKey, meetingChat]);
703
703
  const lines = react.useMemo(
704
- () => chunkFAJLJDWS_cjs.mergeChatLines(history, live, {
704
+ () => chunkFOBB5U4Y_cjs.mergeChatLines(history, live, {
705
705
  myIdentity,
706
706
  nameOf: (identity) => identity === myIdentity ? localParticipant.name || "You" : room.remoteParticipants.get(identity)?.name || null
707
707
  }),
@@ -760,7 +760,7 @@ function useWebinar({
760
760
  const n = ++questionsRead.current;
761
761
  try {
762
762
  const rows = await webinarApi().questions.list(callExtId, linkVersion);
763
- const opened = await Promise.all(rows.map((q) => chunkFAJLJDWS_cjs.openQuestion(q, callKey)));
763
+ const opened = await Promise.all(rows.map((q) => chunkFOBB5U4Y_cjs.openQuestion(q, callKey)));
764
764
  if (n === questionsRead.current) setQuestions(opened);
765
765
  } catch (err) {
766
766
  console.warn("[calls] could not load the questions", err);
@@ -772,7 +772,7 @@ function useWebinar({
772
772
  }, [refresh, refreshQuestions]);
773
773
  react.useEffect(() => {
774
774
  if (!enabled) return;
775
- return chunkFAJLJDWS_cjs.onCallEvent("call.webinar", (m) => {
775
+ return chunkFOBB5U4Y_cjs.onCallEvent("call.webinar", (m) => {
776
776
  if (m.call_ext_id !== callExtId) return;
777
777
  setState((s) => s ? { ...s, live: m.live, chat: m.chat } : s);
778
778
  });
@@ -781,8 +781,8 @@ function useWebinar({
781
781
  react.useEffect(() => {
782
782
  if (!enabled) return;
783
783
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
784
- if (topic !== chunkFAJLJDWS_cjs.CALL_WEBINAR_TOPIC) return;
785
- const packet = chunkFAJLJDWS_cjs.parseWebinarPacket(payload, !!(participant || participantIdentity));
784
+ if (topic !== chunkFOBB5U4Y_cjs.CALL_WEBINAR_TOPIC) return;
785
+ const packet = chunkFOBB5U4Y_cjs.parseWebinarPacket(payload, !!(participant || participantIdentity));
786
786
  if (!packet) return;
787
787
  switch (packet.type) {
788
788
  case "state":
@@ -803,7 +803,7 @@ function useWebinar({
803
803
  return;
804
804
  case "demoted":
805
805
  setStageCheck(false);
806
- chunkFAJLJDWS_cjs.markRejoining(callExtId);
806
+ chunkFOBB5U4Y_cjs.markRejoining(callExtId);
807
807
  if (fallback.current) clearTimeout(fallback.current);
808
808
  fallback.current = setTimeout(() => void room.disconnect(), DEMOTE_REJOIN_FALLBACK_MS);
809
809
  return;
@@ -830,12 +830,12 @@ function useWebinar({
830
830
  [callExtId, linkVersion]
831
831
  );
832
832
  const stepDown = react.useCallback(async () => {
833
- chunkFAJLJDWS_cjs.markRejoining(callExtId);
833
+ chunkFOBB5U4Y_cjs.markRejoining(callExtId);
834
834
  setStageCheck(false);
835
835
  try {
836
836
  await webinarApi().demote(callExtId, myIdentity, linkVersion);
837
837
  } catch (err) {
838
- chunkFAJLJDWS_cjs.markRejoining(callExtId, false);
838
+ chunkFOBB5U4Y_cjs.markRejoining(callExtId, false);
839
839
  failed("leave the stage")(err);
840
840
  }
841
841
  }, [callExtId, myIdentity, linkVersion]);
@@ -895,7 +895,7 @@ function useWebinar({
895
895
  if (!callKey) throw new Error("This call has no key.");
896
896
  await webinarApi().questions.ask(
897
897
  callExtId,
898
- await chunkFAJLJDWS_cjs.sealQuestionText(text, callKey),
898
+ await chunkFOBB5U4Y_cjs.sealQuestionText(text, callKey),
899
899
  linkVersion
900
900
  );
901
901
  }, "ask the question"),
@@ -909,7 +909,7 @@ function useWebinar({
909
909
  await webinarApi().questions.update(
910
910
  callExtId,
911
911
  id,
912
- { answer: await chunkFAJLJDWS_cjs.sealQuestionText(text, callKey) },
912
+ { answer: await chunkFOBB5U4Y_cjs.sealQuestionText(text, callKey) },
913
913
  linkVersion
914
914
  );
915
915
  }, "answer the question"),
@@ -939,15 +939,15 @@ function useWebinar({
939
939
  function useHostMuteNotice(callExtId) {
940
940
  const room = componentsReact.useRoomContext();
941
941
  react.useEffect(() => {
942
- const notifyMuted = chunkFAJLJDWS_cjs.createMutedNotifier((text) => chunkIX4HOS5Z_cjs.notify.info(text));
942
+ const notifyMuted = chunkFOBB5U4Y_cjs.createMutedNotifier((text) => chunkIX4HOS5Z_cjs.notify.info(text));
943
943
  const onData = (payload, participant, _kind, topic, _encryption, participantIdentity) => {
944
- if (topic !== chunkFAJLJDWS_cjs.CALL_MODERATION_TOPIC) return;
944
+ if (topic !== chunkFOBB5U4Y_cjs.CALL_MODERATION_TOPIC) return;
945
945
  if (participant || participantIdentity) return;
946
- const notice = chunkFAJLJDWS_cjs.mutedNoticeFromPacket(payload);
946
+ const notice = chunkFOBB5U4Y_cjs.mutedNoticeFromPacket(payload);
947
947
  if (notice) notifyMuted(notice);
948
948
  };
949
949
  room.on(livekitClient.RoomEvent.DataReceived, onData);
950
- const offWs = chunkFAJLJDWS_cjs.onCallEvent("call.muted", (message) => {
950
+ const offWs = chunkFOBB5U4Y_cjs.onCallEvent("call.muted", (message) => {
951
951
  if (message.callExtId === callExtId) notifyMuted(message);
952
952
  });
953
953
  return () => {
@@ -5,9 +5,9 @@ function registerDocumentTurnFlush(documentId, flush) {
5
5
  if (flushes.get(documentId) === flush) flushes.delete(documentId);
6
6
  };
7
7
  }
8
- function flushDocumentForTurn(documentId) {
9
- const flush = documentId ? flushes.get(documentId) : void 0;
10
- return flush ? flush() : null;
8
+ function flushDocumentForTurn(_documentId) {
9
+ const pending = [...flushes.values()];
10
+ return pending.length ? Promise.all(pending.map((flush) => flush())).then(() => void 0) : null;
11
11
  }
12
12
  export {
13
13
  flushDocumentForTurn,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/chat/turn-flush.ts"],
4
- "sourcesContent": ["/**\n * Saving a document before a chat turn works on it. An editor whose changes live only on screen\n * (the slides editor: no live co-editing) registers how to save them; a turn that names that\n * document (`active_artifact_ext_id`) awaits it first, so ARBI reads and edits what the person\n * sees, not the version saved before their last changes.\n */\n\ntype Flush = () => Promise<void>\n\nconst flushes = new Map<string, Flush>()\n\n/** While mounted, `flush` saves `documentId`'s unsaved changes; returns the unregister. */\nexport function registerDocumentTurnFlush(documentId: string, flush: Flush): () => void {\n flushes.set(documentId, flush)\n return () => {\n if (flushes.get(documentId) === flush) flushes.delete(documentId)\n }\n}\n\n/**\n * The save of the document a turn names, if an editor registered one; null otherwise, so a turn\n * about anything else is not delayed at all (not even by a microtask).\n */\nexport function flushDocumentForTurn(documentId: string | null | undefined): Promise<void> | null {\n const flush = documentId ? flushes.get(documentId) : undefined\n return flush ? flush() : null\n}\n"],
5
- "mappings": "AASA,MAAM,UAAU,oBAAI,IAAmB;AAGhC,SAAS,0BAA0B,YAAoB,OAA0B;AACtF,UAAQ,IAAI,YAAY,KAAK;AAC7B,SAAO,MAAM;AACX,QAAI,QAAQ,IAAI,UAAU,MAAM,MAAO,SAAQ,OAAO,UAAU;AAAA,EAClE;AACF;AAMO,SAAS,qBAAqB,YAA6D;AAChG,QAAM,QAAQ,aAAa,QAAQ,IAAI,UAAU,IAAI;AACrD,SAAO,QAAQ,MAAM,IAAI;AAC3B;",
4
+ "sourcesContent": ["/** Mounted editors save their local changes before any chat turn reads the workspace. */\n\ntype Flush = () => Promise<void>\n\nconst flushes = new Map<string, Flush>()\n\n/** While mounted, `flush` saves `documentId`'s unsaved changes; returns the unregister. */\nexport function registerDocumentTurnFlush(documentId: string, flush: Flush): () => void {\n flushes.set(documentId, flush)\n return () => {\n if (flushes.get(documentId) === flush) flushes.delete(documentId)\n }\n}\n\n/** Save every mounted editor before a turn reads workspace documents. */\nexport function flushDocumentForTurn(_documentId: string | null | undefined): Promise<void> | null {\n const pending = [...flushes.values()]\n return pending.length ? Promise.all(pending.map((flush) => flush())).then(() => undefined) : null\n}\n"],
5
+ "mappings": "AAIA,MAAM,UAAU,oBAAI,IAAmB;AAGhC,SAAS,0BAA0B,YAAoB,OAA0B;AACtF,UAAQ,IAAI,YAAY,KAAK;AAC7B,SAAO,MAAM;AACX,QAAI,QAAQ,IAAI,UAAU,MAAM,MAAO,SAAQ,OAAO,UAAU;AAAA,EAClE;AACF;AAGO,SAAS,qBAAqB,aAA8D;AACjG,QAAM,UAAU,CAAC,GAAG,QAAQ,OAAO,CAAC;AACpC,SAAO,QAAQ,SAAS,QAAQ,IAAI,QAAQ,IAAI,CAAC,UAAU,MAAM,CAAC,CAAC,EAAE,KAAK,MAAM,MAAS,IAAI;AAC/F;",
6
6
  "names": []
7
7
  }
@@ -2,7 +2,7 @@
2
2
 
3
3
  var chunkIX4HOS5Z_cjs = require('./chunk-IX4HOS5Z.cjs');
4
4
  var chunkN73IMVFF_cjs = require('./chunk-N73IMVFF.cjs');
5
- var chunkNDIKMOBJ_cjs = require('./chunk-NDIKMOBJ.cjs');
5
+ var chunkKULS4YWP_cjs = require('./chunk-KULS4YWP.cjs');
6
6
  var chunkKC474WVG_cjs = require('./chunk-KC474WVG.cjs');
7
7
  var chunkHJDR5HEO_cjs = require('./chunk-HJDR5HEO.cjs');
8
8
  var chunk5NOZDH3F_cjs = require('./chunk-5NOZDH3F.cjs');
@@ -505,7 +505,7 @@ function takeDocumentForTurn(input) {
505
505
  const { documentId, entry } = picked;
506
506
  if (entry) {
507
507
  const { shownId, showLive } = entry.current();
508
- if (shownId && shownId !== documentId && chunkNDIKMOBJ_cjs.parseVersionRef(shownId).docId === documentId)
508
+ if (shownId && shownId !== documentId && chunkKULS4YWP_cjs.parseVersionRef(shownId).docId === documentId)
509
509
  showLive?.();
510
510
  }
511
511
  return documentId;
@@ -593,5 +593,5 @@ exports.useAssistantName = useAssistantName;
593
593
  exports.useOpenDocumentId = useOpenDocumentId;
594
594
  exports.useRegisterOpenDocument = useRegisterOpenDocument;
595
595
  exports.useTurnDocumentId = useTurnDocumentId;
596
- //# sourceMappingURL=chunk-WUYSVTKE.cjs.map
597
- //# sourceMappingURL=chunk-WUYSVTKE.cjs.map
596
+ //# sourceMappingURL=chunk-6RRKO4ZK.cjs.map
597
+ //# sourceMappingURL=chunk-6RRKO4ZK.cjs.map