@opengeni/react 7.4.0 → 7.5.0-canary.36927100590001
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 +124 -27
- package/dist/artifacts-document.js +3 -2
- package/dist/artifacts-presentation.js +3 -2
- package/dist/artifacts-spreadsheet.js +2 -2
- package/dist/artifacts.d.ts +1 -0
- package/dist/artifacts.js +516 -1413
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-2E6R2Y4W.js → browser-viewer-Z7S7QEJL.js} +6 -5
- package/dist/chat-interactive-block-UGBTNB3L.js +11 -0
- package/dist/{chunk-Y3QTZN2F.js → chunk-2VFNCX4E.js} +6 -6
- package/dist/{chunk-UDD6DLLT.js → chunk-3IJJEYW4.js} +15 -3
- package/dist/chunk-3IJJEYW4.js.map +1 -0
- package/dist/chunk-4RVYOY4O.js +379 -0
- package/dist/chunk-4RVYOY4O.js.map +1 -0
- package/dist/chunk-5CVXZA3Y.js +292 -0
- package/dist/chunk-5CVXZA3Y.js.map +1 -0
- package/dist/{chunk-5YK2ZDIG.js → chunk-5J4FOW6Q.js} +2 -10
- package/dist/chunk-5J4FOW6Q.js.map +1 -0
- package/dist/{chunk-KVERXCFY.js → chunk-5KYZIQFY.js} +40 -3
- package/dist/chunk-5KYZIQFY.js.map +1 -0
- package/dist/{chunk-KNDIXZN6.js → chunk-7UAMYXJY.js} +25 -18
- package/dist/{chunk-KNDIXZN6.js.map → chunk-7UAMYXJY.js.map} +1 -1
- package/dist/{chunk-SEEVCT36.js → chunk-A4UJB2MM.js} +2 -2
- package/dist/{chunk-X4HIN6TV.js → chunk-AFLPWUQ7.js} +71 -162
- package/dist/chunk-AFLPWUQ7.js.map +1 -0
- package/dist/chunk-B3V6TG77.js +41 -0
- package/dist/chunk-B3V6TG77.js.map +1 -0
- package/dist/{chunk-XEJEOAOV.js → chunk-B7WMS3CS.js} +51 -40
- package/dist/chunk-B7WMS3CS.js.map +1 -0
- package/dist/{chunk-S5JQ4R22.js → chunk-BDKR6BAR.js} +54 -24
- package/dist/chunk-BDKR6BAR.js.map +1 -0
- package/dist/{chunk-4MDLEHXL.js → chunk-DYWBUX5Q.js} +24 -12
- package/dist/chunk-DYWBUX5Q.js.map +1 -0
- package/dist/{chunk-YZ3YYMOM.js → chunk-E3G7SAYZ.js} +252 -624
- package/dist/chunk-E3G7SAYZ.js.map +1 -0
- package/dist/chunk-JT4JW7BC.js +54 -0
- package/dist/chunk-JT4JW7BC.js.map +1 -0
- package/dist/{chunk-5AZSK6MV.js → chunk-LWNLETQT.js} +5 -5
- package/dist/{chunk-USXVQACU.js → chunk-M7LBYP63.js} +5 -4
- package/dist/{chunk-USXVQACU.js.map → chunk-M7LBYP63.js.map} +1 -1
- package/dist/{chunk-L4OSXQ6H.js → chunk-NBOHNRLJ.js} +1289 -1060
- package/dist/chunk-NBOHNRLJ.js.map +1 -0
- package/dist/chunk-RHREZ7BO.js +11 -0
- package/dist/chunk-RHREZ7BO.js.map +1 -0
- package/dist/chunk-SCOTPE2I.js +589 -0
- package/dist/chunk-SCOTPE2I.js.map +1 -0
- package/dist/chunk-WEHMPOXK.js +39 -0
- package/dist/chunk-WEHMPOXK.js.map +1 -0
- package/dist/chunk-WRHIZYZF.js +988 -0
- package/dist/chunk-WRHIZYZF.js.map +1 -0
- package/dist/{chunk-3E2AITTZ.js → chunk-WUISZ3WU.js} +32 -28
- package/dist/{chunk-3E2AITTZ.js.map → chunk-WUISZ3WU.js.map} +1 -1
- package/dist/chunk-XCHWDJ22.js +107 -0
- package/dist/chunk-XCHWDJ22.js.map +1 -0
- package/dist/{chunk-DUEBTL3N.js → chunk-Y2PRMYLC.js} +4 -3
- package/dist/{chunk-DUEBTL3N.js.map → chunk-Y2PRMYLC.js.map} +1 -1
- package/dist/chunk-YBXQDQFX.js +1772 -0
- package/dist/chunk-YBXQDQFX.js.map +1 -0
- package/dist/chunk-YV4L3ILF.js +9 -0
- package/dist/chunk-YV4L3ILF.js.map +1 -0
- package/dist/{chunk-V2ABIVYJ.js → chunk-ZXSEDZP7.js} +70 -6
- package/dist/chunk-ZXSEDZP7.js.map +1 -0
- package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
- package/dist/components/artifacts/artifact-sandbox.d.ts +27 -0
- package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
- package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
- package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
- package/dist/components/artifacts/editable-artifact-view.d.ts +46 -0
- package/dist/components/artifacts/index.d.ts +7 -0
- package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
- package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
- package/dist/components/artifacts/site-view.d.ts +23 -0
- package/dist/components/browser-select-control.d.ts +3 -1
- package/dist/components/code-editor.d.ts +1 -4
- package/dist/components/desktop-viewer.d.ts +1 -1
- package/dist/components/message-timeline.d.ts +20 -3
- package/dist/components/open-geni-links.d.ts +16 -0
- package/dist/components/session-conversation.d.ts +20 -1
- package/dist/components/timeline-pill-geometry.d.ts +8 -0
- package/dist/components/timeline-pill-placement.d.ts +24 -0
- package/dist/components/usage-limit-notice.d.ts +49 -0
- package/dist/components/usage-member-list.d.ts +91 -0
- package/dist/components/usage-meter.d.ts +73 -0
- package/dist/composer.js +5 -5
- package/dist/{computer-viewer-WMJE6OA3.js → computer-viewer-KJWLH3GI.js} +7 -5
- package/dist/connect.js +7 -6
- package/dist/connect.js.map +1 -1
- package/dist/conversation-timeline.d.ts +1 -1
- package/dist/desktop.d.ts +7 -0
- package/dist/desktop.js +26 -0
- package/dist/desktop.js.map +1 -0
- package/dist/editor.d.ts +7 -0
- package/dist/editor.js +21 -0
- package/dist/editor.js.map +1 -0
- package/dist/hooks/latest-question.d.ts +1 -1
- package/dist/hooks/use-composer.d.ts +4 -0
- package/dist/hooks/use-desktop-stream.d.ts +1 -1
- package/dist/hooks/use-usage.d.ts +40 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.js +460 -1386
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +8 -6
- package/dist/interaction.js.map +1 -1
- package/dist/{latest-question-QB4EUCW5.js → latest-question-IS445HOW.js} +3 -3
- package/dist/lib/format.d.ts +6 -1
- package/dist/lib/workbench-peers.d.ts +36 -0
- package/dist/machines.js +2 -2
- package/dist/{markdown-table-layout-PFUEZ2U4.js → markdown-table-layout-QXSLK4BY.js} +37 -5
- package/dist/markdown-table-layout-QXSLK4BY.js.map +1 -0
- package/dist/{queue-surface-implementation-IM2HD3SK.js → queue-surface-implementation-2CAQAIHM.js} +2 -2
- package/dist/realtime.js +3 -3
- package/dist/{sandbox-files-PXPIZAFG.js → sandbox-files-AEJTWGT4.js} +8 -5
- package/dist/sandbox-files-AEJTWGT4.js.map +1 -0
- package/dist/session-ui.d.ts +9 -2
- package/dist/session-ui.js +39 -20
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +4 -4
- package/dist/sites.js +4 -289
- package/dist/sites.js.map +1 -1
- package/dist/terminal.d.ts +8 -0
- package/dist/terminal.js +31 -0
- package/dist/terminal.js.map +1 -0
- package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
- package/dist/timeline/genie-loading.d.ts +3 -1
- package/dist/timeline/types.d.ts +8 -1
- package/dist/{timeline-annotation-selection-ODLVF4OI.js → timeline-annotation-selection-DMLMM2TX.js} +6 -1
- package/dist/timeline-annotation-selection-DMLMM2TX.js.map +1 -0
- package/dist/timeline-pill-geometry-Z23V243M.js +110 -0
- package/dist/timeline-pill-geometry-Z23V243M.js.map +1 -0
- package/dist/usage/allowance-copy.d.ts +25 -0
- package/dist/usage/summary.d.ts +35 -0
- package/dist/usage.d.ts +13 -0
- package/dist/usage.js +1073 -0
- package/dist/usage.js.map +1 -0
- package/package.json +19 -3
- package/src/artifacts.ts +37 -0
- package/src/components/artifacts/artifact-chrome.tsx +413 -0
- package/src/components/artifacts/artifact-sandbox.tsx +185 -0
- package/src/components/artifacts/chat-interactive-block.tsx +260 -0
- package/src/components/artifacts/deferred-chat-media.tsx +67 -0
- package/src/components/artifacts/editable-artifact-ui.tsx +3 -1
- package/src/components/artifacts/editable-artifact-view.tsx +187 -0
- package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
- package/src/components/artifacts/editable-document.tsx +34 -36
- package/src/components/artifacts/editable-presentation.tsx +21 -16
- package/src/components/artifacts/index.ts +41 -0
- package/src/components/artifacts/latest-task-runner.ts +49 -0
- package/src/components/artifacts/session-artifact-viewer.tsx +376 -0
- package/src/components/artifacts/site-view.tsx +116 -0
- package/src/components/browser-select-control.tsx +4 -1
- package/src/components/browser-viewer.tsx +81 -22
- package/src/components/code-editor.tsx +13 -16
- package/src/components/computer-viewer.tsx +51 -23
- package/src/components/desktop-viewer.tsx +1 -1
- package/src/components/markdown-table-layout.ts +49 -5
- package/src/components/markdown.tsx +5 -2
- package/src/components/message-timeline.tsx +557 -516
- package/src/components/open-geni-links.tsx +24 -0
- package/src/components/sandbox-terminal.tsx +48 -11
- package/src/components/session-chrome.tsx +22 -3
- package/src/components/session-conversation.tsx +195 -102
- package/src/components/timeline-annotation-selection.tsx +7 -0
- package/src/components/timeline-pill-geometry.ts +169 -0
- package/src/components/timeline-pill-placement.ts +99 -0
- package/src/components/usage-limit-notice.tsx +179 -0
- package/src/components/usage-member-list.tsx +792 -0
- package/src/components/usage-meter.tsx +382 -0
- package/src/conversation-timeline.ts +10 -2
- package/src/desktop.ts +24 -0
- package/src/editor.ts +13 -0
- package/src/hooks/use-available-models.ts +1 -1
- package/src/hooks/use-composer.ts +57 -2
- package/src/hooks/use-desktop-stream.ts +19 -28
- package/src/hooks/use-usage.ts +100 -0
- package/src/index.ts +22 -0
- package/src/lib/format.ts +32 -6
- package/src/lib/workbench-peers.ts +62 -0
- package/src/session-ui.ts +22 -0
- package/src/terminal.ts +26 -0
- package/src/timeline/activity-rail.tsx +5 -3
- package/src/timeline/allowance-exhausted-row.tsx +86 -0
- package/src/timeline/genie-loading.tsx +16 -6
- package/src/timeline/projection.ts +78 -4
- package/src/timeline/types.ts +8 -0
- package/src/usage/allowance-copy.ts +64 -0
- package/src/usage/summary.ts +93 -0
- package/src/usage.ts +50 -0
- package/styles/compiled.css +325 -0
- package/dist/chunk-4MDLEHXL.js.map +0 -1
- package/dist/chunk-5YK2ZDIG.js.map +0 -1
- package/dist/chunk-KVERXCFY.js.map +0 -1
- package/dist/chunk-L4OSXQ6H.js.map +0 -1
- package/dist/chunk-S5JQ4R22.js.map +0 -1
- package/dist/chunk-UDD6DLLT.js.map +0 -1
- package/dist/chunk-UGMKPUEM.js +0 -299
- package/dist/chunk-UGMKPUEM.js.map +0 -1
- package/dist/chunk-V2ABIVYJ.js.map +0 -1
- package/dist/chunk-X4HIN6TV.js.map +0 -1
- package/dist/chunk-XEJEOAOV.js.map +0 -1
- package/dist/chunk-YZ3YYMOM.js.map +0 -1
- package/dist/markdown-table-layout-PFUEZ2U4.js.map +0 -1
- package/dist/timeline-annotation-selection-ODLVF4OI.js.map +0 -1
- /package/dist/{browser-viewer-2E6R2Y4W.js.map → browser-viewer-Z7S7QEJL.js.map} +0 -0
- /package/dist/{computer-viewer-WMJE6OA3.js.map → chat-interactive-block-UGBTNB3L.js.map} +0 -0
- /package/dist/{chunk-Y3QTZN2F.js.map → chunk-2VFNCX4E.js.map} +0 -0
- /package/dist/{chunk-SEEVCT36.js.map → chunk-A4UJB2MM.js.map} +0 -0
- /package/dist/{chunk-5AZSK6MV.js.map → chunk-LWNLETQT.js.map} +0 -0
- /package/dist/{sandbox-files-PXPIZAFG.js.map → computer-viewer-KJWLH3GI.js.map} +0 -0
- /package/dist/{latest-question-QB4EUCW5.js.map → latest-question-IS445HOW.js.map} +0 -0
- /package/dist/{queue-surface-implementation-IM2HD3SK.js.map → queue-surface-implementation-2CAQAIHM.js.map} +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import type { OpenGeniClient, ToolGatewayIdentity } from "@opengeni/sdk";
|
|
2
|
+
import { useEffect, useMemo, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { loadSiteSnapshot } from "../../sites-ui";
|
|
5
|
+
import { useOpenGeniLinkResolver } from "../open-geni-links";
|
|
6
|
+
import {
|
|
7
|
+
ArtifactButton,
|
|
8
|
+
ArtifactLabelsProvider,
|
|
9
|
+
ArtifactSelect,
|
|
10
|
+
useArtifactLabels,
|
|
11
|
+
type ArtifactLabels,
|
|
12
|
+
} from "./artifact-chrome";
|
|
13
|
+
import { ArtifactSandbox } from "./artifact-sandbox";
|
|
14
|
+
import { DeferredChatMedia } from "./deferred-chat-media";
|
|
15
|
+
import { inlineHtmlDocument } from "./inline-html-document";
|
|
16
|
+
import type { PublishedHtmlArtifactToolBridge } from "./published-html-artifact-frame";
|
|
17
|
+
|
|
18
|
+
// Fixed chat viewports keep async Site loads and fragment resize messages from
|
|
19
|
+
// moving the conversation. Full-screen remains available for larger content.
|
|
20
|
+
const INLINE_PREVIEW_HEIGHT = 360;
|
|
21
|
+
const SITE_PREVIEW_HEIGHT = 400;
|
|
22
|
+
const PREVIEW_CHROME_HEIGHT = 50;
|
|
23
|
+
|
|
24
|
+
/** Reads a Site needs to render; the console client, the SDK client, or a proxy. */
|
|
25
|
+
export type SiteSnapshotClient = Pick<
|
|
26
|
+
OpenGeniClient,
|
|
27
|
+
"getWorkspaceArtifact" | "getWorkspaceArtifactHtml"
|
|
28
|
+
> &
|
|
29
|
+
Partial<Pick<OpenGeniClient, "getWorkspaceArtifactContent">>;
|
|
30
|
+
|
|
31
|
+
/** The exact saved Site a tool bridge is pinned to (absent for inline HTML). */
|
|
32
|
+
export type SiteToolScope = {
|
|
33
|
+
artifactId: string;
|
|
34
|
+
siteVersionId: string;
|
|
35
|
+
requestedTools: readonly ToolGatewayIdentity[];
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** Host-owned Site API access. Omit it and Sites render without workspace tools. */
|
|
39
|
+
export type SiteToolBridgeFactory = (
|
|
40
|
+
site?: SiteToolScope,
|
|
41
|
+
) => PublishedHtmlArtifactToolBridge | undefined;
|
|
42
|
+
|
|
43
|
+
export type ChatInteractiveBlockProps = {
|
|
44
|
+
workspaceId: string;
|
|
45
|
+
kind: "html" | "site";
|
|
46
|
+
content: string;
|
|
47
|
+
client: SiteSnapshotClient;
|
|
48
|
+
toolBridge?: SiteToolBridgeFactory | undefined;
|
|
49
|
+
theme?: "light" | "dark" | undefined;
|
|
50
|
+
/** Translations for the preview's copy; defaults to English. */
|
|
51
|
+
labels?: Partial<ArtifactLabels> | undefined;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Inline preview for an assistant `opengeni-html` or `opengeni-site` fence, as
|
|
56
|
+
* the OpenGeni console renders it. "Open Site" goes through the nearest
|
|
57
|
+
* `resolveLink`, so a host decides where a Site opens.
|
|
58
|
+
*/
|
|
59
|
+
export function ChatInteractiveBlock({ labels, ...props }: ChatInteractiveBlockProps) {
|
|
60
|
+
return (
|
|
61
|
+
<ArtifactLabelsProvider labels={labels}>
|
|
62
|
+
<InteractiveBlock {...props} />
|
|
63
|
+
</ArtifactLabelsProvider>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function InteractiveBlock(props: ChatInteractiveBlockProps) {
|
|
68
|
+
const labels = useArtifactLabels();
|
|
69
|
+
const height =
|
|
70
|
+
(props.kind === "html" ? INLINE_PREVIEW_HEIGHT : SITE_PREVIEW_HEIGHT) + PREVIEW_CHROME_HEIGHT;
|
|
71
|
+
return (
|
|
72
|
+
<DeferredChatMedia
|
|
73
|
+
key={`${props.workspaceId}:${props.kind}:${props.kind === "site" ? props.content : "inline"}`}
|
|
74
|
+
height={height}
|
|
75
|
+
actionLabel={props.kind === "html" ? labels.loadPreview : labels.loadSitePreview}
|
|
76
|
+
>
|
|
77
|
+
<div style={{ height }}>
|
|
78
|
+
<LoadedChatInteractiveBlock {...props} />
|
|
79
|
+
</div>
|
|
80
|
+
</DeferredChatMedia>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function LoadedChatInteractiveBlock(props: ChatInteractiveBlockProps) {
|
|
85
|
+
const labels = useArtifactLabels();
|
|
86
|
+
if (props.kind === "html") return <InlineHtml {...props} />;
|
|
87
|
+
try {
|
|
88
|
+
const value = JSON.parse(props.content);
|
|
89
|
+
if (
|
|
90
|
+
!value ||
|
|
91
|
+
typeof value.siteId !== "string" ||
|
|
92
|
+
!/^[0-9a-f-]{36}$/i.test(value.siteId) ||
|
|
93
|
+
(value.versionId !== undefined &&
|
|
94
|
+
(typeof value.versionId !== "string" || !/^[0-9a-f-]{36}$/i.test(value.versionId)))
|
|
95
|
+
)
|
|
96
|
+
throw new Error("Invalid Site reference");
|
|
97
|
+
return (
|
|
98
|
+
<SiteEmbed
|
|
99
|
+
key={`${props.workspaceId}:${value.siteId}:${value.versionId ?? "current"}`}
|
|
100
|
+
{...props}
|
|
101
|
+
siteId={value.siteId}
|
|
102
|
+
initialVersionId={value.versionId}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
} catch {
|
|
106
|
+
return <p role="alert">{labels.siteReferenceInvalid}</p>;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function InlineHtml({ content, toolBridge, theme }: ChatInteractiveBlockProps) {
|
|
111
|
+
const labels = useArtifactLabels();
|
|
112
|
+
const html = useMemo(() => inlineHtmlDocument(content), [content]);
|
|
113
|
+
const bridge = useMemo(() => toolBridge?.(), [toolBridge]);
|
|
114
|
+
return (
|
|
115
|
+
<ArtifactSandbox
|
|
116
|
+
title={labels.preview}
|
|
117
|
+
showTitle={false}
|
|
118
|
+
showLiveStatus={false}
|
|
119
|
+
html={html}
|
|
120
|
+
toolBridge={bridge}
|
|
121
|
+
height={INLINE_PREVIEW_HEIGHT}
|
|
122
|
+
className="h-full"
|
|
123
|
+
fill
|
|
124
|
+
theme={theme}
|
|
125
|
+
/>
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function SiteEmbed(
|
|
130
|
+
props: ChatInteractiveBlockProps & { siteId: string; initialVersionId?: string },
|
|
131
|
+
) {
|
|
132
|
+
const [versionId, setVersionId] = useState(props.initialVersionId);
|
|
133
|
+
return (
|
|
134
|
+
<SiteEmbedContent
|
|
135
|
+
key={versionId ?? "current"}
|
|
136
|
+
{...props}
|
|
137
|
+
versionId={versionId}
|
|
138
|
+
onVersionChange={setVersionId}
|
|
139
|
+
/>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function SiteEmbedContent({
|
|
144
|
+
workspaceId,
|
|
145
|
+
client,
|
|
146
|
+
toolBridge,
|
|
147
|
+
theme,
|
|
148
|
+
siteId,
|
|
149
|
+
versionId,
|
|
150
|
+
onVersionChange,
|
|
151
|
+
}: ChatInteractiveBlockProps & {
|
|
152
|
+
siteId: string;
|
|
153
|
+
versionId?: string | undefined;
|
|
154
|
+
onVersionChange: (id: string) => void;
|
|
155
|
+
}) {
|
|
156
|
+
const [loaded, setLoaded] = useState<{
|
|
157
|
+
client: SiteSnapshotClient;
|
|
158
|
+
snapshot: Awaited<ReturnType<typeof loadSiteSnapshot>>;
|
|
159
|
+
} | null>(null);
|
|
160
|
+
const snapshot = loaded?.client === client ? loaded.snapshot : null;
|
|
161
|
+
const [error, setError] = useState(false);
|
|
162
|
+
const [retry, setRetry] = useState(0);
|
|
163
|
+
useEffect(() => {
|
|
164
|
+
const abort = new AbortController();
|
|
165
|
+
setError(false);
|
|
166
|
+
setLoaded(null);
|
|
167
|
+
void loadSiteSnapshot(client, workspaceId, siteId, {
|
|
168
|
+
signal: abort.signal,
|
|
169
|
+
...(versionId ? { versionId } : {}),
|
|
170
|
+
})
|
|
171
|
+
.then((value) => {
|
|
172
|
+
if (!abort.signal.aborted) setLoaded({ client, snapshot: value });
|
|
173
|
+
})
|
|
174
|
+
.catch(() => {
|
|
175
|
+
if (!abort.signal.aborted) setError(true);
|
|
176
|
+
});
|
|
177
|
+
return () => abort.abort();
|
|
178
|
+
}, [client, workspaceId, siteId, versionId, retry]);
|
|
179
|
+
const content = snapshot?.content;
|
|
180
|
+
const bridge = useMemo(
|
|
181
|
+
() =>
|
|
182
|
+
content
|
|
183
|
+
? toolBridge?.({
|
|
184
|
+
artifactId: siteId,
|
|
185
|
+
siteVersionId: content.versionId,
|
|
186
|
+
requestedTools: content.requestedTools,
|
|
187
|
+
})
|
|
188
|
+
: undefined,
|
|
189
|
+
[toolBridge, siteId, content],
|
|
190
|
+
);
|
|
191
|
+
const open = useOpenGeniLinkResolver()?.({ kind: "site", artifactId: siteId, workspaceId });
|
|
192
|
+
const labels = useArtifactLabels();
|
|
193
|
+
if (error)
|
|
194
|
+
return (
|
|
195
|
+
<p role="alert">
|
|
196
|
+
{labels.siteLoadFailed}{" "}
|
|
197
|
+
<ArtifactButton size="sm" onClick={() => setRetry((v) => v + 1)}>
|
|
198
|
+
{labels.retry}
|
|
199
|
+
</ArtifactButton>
|
|
200
|
+
</p>
|
|
201
|
+
);
|
|
202
|
+
if (!snapshot) return <p role="status">{labels.loadingSite}</p>;
|
|
203
|
+
if (!content) return <p>{labels.siteUnpublished}</p>;
|
|
204
|
+
return (
|
|
205
|
+
<ArtifactSandbox
|
|
206
|
+
title={snapshot.detail.artifact.title}
|
|
207
|
+
html={content.html}
|
|
208
|
+
toolBridge={bridge}
|
|
209
|
+
height={SITE_PREVIEW_HEIGHT}
|
|
210
|
+
className="h-full"
|
|
211
|
+
fill
|
|
212
|
+
theme={theme}
|
|
213
|
+
headerControls={
|
|
214
|
+
<>
|
|
215
|
+
{/* One published version has nothing to choose; keep the bar for the title. */}
|
|
216
|
+
{snapshot.detail.versions.length > 1 ||
|
|
217
|
+
!snapshot.detail.versions.some((v) => v.id === content.versionId) ? (
|
|
218
|
+
<ArtifactSelect
|
|
219
|
+
aria-label={labels.siteVersion}
|
|
220
|
+
className="bg-transparent text-xs"
|
|
221
|
+
value={content.versionId}
|
|
222
|
+
onChange={(e) => onVersionChange(e.target.value)}
|
|
223
|
+
>
|
|
224
|
+
{!snapshot.detail.versions.some((v) => v.id === content.versionId) && (
|
|
225
|
+
<option value={content.versionId}>{labels.savedVersion}</option>
|
|
226
|
+
)}
|
|
227
|
+
{snapshot.detail.versions.map((v) => (
|
|
228
|
+
<option key={v.id} value={v.id}>
|
|
229
|
+
{labels.version(v.revision)}
|
|
230
|
+
</option>
|
|
231
|
+
))}
|
|
232
|
+
</ArtifactSelect>
|
|
233
|
+
) : null}
|
|
234
|
+
{open?.href ? (
|
|
235
|
+
<a
|
|
236
|
+
className="shrink-0 whitespace-nowrap text-xs underline"
|
|
237
|
+
href={open.href}
|
|
238
|
+
data-og-open-site=""
|
|
239
|
+
>
|
|
240
|
+
{labels.openSite}
|
|
241
|
+
</a>
|
|
242
|
+
) : open?.open ? (
|
|
243
|
+
<button
|
|
244
|
+
type="button"
|
|
245
|
+
className="shrink-0 cursor-pointer whitespace-nowrap text-xs underline"
|
|
246
|
+
data-og-open-site=""
|
|
247
|
+
onClick={() =>
|
|
248
|
+
void Promise.resolve()
|
|
249
|
+
.then(open.open)
|
|
250
|
+
.catch(() => undefined)
|
|
251
|
+
}
|
|
252
|
+
>
|
|
253
|
+
{labels.openSite}
|
|
254
|
+
</button>
|
|
255
|
+
) : null}
|
|
256
|
+
</>
|
|
257
|
+
}
|
|
258
|
+
/>
|
|
259
|
+
);
|
|
260
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { useEffect, useRef, useState, type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Reserve the chat slot before starting expensive media work. Activation is
|
|
5
|
+
* one-way: scrolling away must not discard an interactive preview's state.
|
|
6
|
+
* The timeline parks at its initial tip in layout effects before this observer
|
|
7
|
+
* is attached, so historical previews above the entry viewport stay dormant.
|
|
8
|
+
*/
|
|
9
|
+
export function DeferredChatMedia({
|
|
10
|
+
children,
|
|
11
|
+
height,
|
|
12
|
+
label,
|
|
13
|
+
actionLabel,
|
|
14
|
+
}: {
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
height: number;
|
|
17
|
+
/** What is loading, as in "Load {label}". */
|
|
18
|
+
label?: string | undefined;
|
|
19
|
+
/** The complete (translated) activation label; overrides `label`. */
|
|
20
|
+
actionLabel?: string | undefined;
|
|
21
|
+
}) {
|
|
22
|
+
const host = useRef<HTMLDivElement>(null);
|
|
23
|
+
const [active, setActive] = useState(false);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const node = host.current;
|
|
26
|
+
if (active || !node) return;
|
|
27
|
+
if (typeof IntersectionObserver === "undefined") {
|
|
28
|
+
setActive(true);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
let disposed = false;
|
|
32
|
+
const observer = new IntersectionObserver(
|
|
33
|
+
(entries) => {
|
|
34
|
+
if (disposed || !entries.some((entry) => entry.isIntersecting)) return;
|
|
35
|
+
setActive(true);
|
|
36
|
+
observer.disconnect();
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
root: node.closest("[data-og-timeline-scroller]"),
|
|
40
|
+
rootMargin: "200px 0px",
|
|
41
|
+
},
|
|
42
|
+
);
|
|
43
|
+
observer.observe(node);
|
|
44
|
+
return () => {
|
|
45
|
+
disposed = true;
|
|
46
|
+
observer.disconnect();
|
|
47
|
+
};
|
|
48
|
+
}, [active]);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<div ref={host} data-chat-media={active ? "active" : "deferred"}>
|
|
52
|
+
{active ? (
|
|
53
|
+
children
|
|
54
|
+
) : (
|
|
55
|
+
<div className="flex w-full items-center justify-center" style={{ height }}>
|
|
56
|
+
<button
|
|
57
|
+
type="button"
|
|
58
|
+
className="rounded-md px-3 py-2 text-sm text-fg-muted hover:text-fg focus-visible:outline-2 focus-visible:outline-ring"
|
|
59
|
+
onClick={() => setActive(true)}
|
|
60
|
+
>
|
|
61
|
+
{actionLabel ?? `Load ${label ?? "media"}`}
|
|
62
|
+
</button>
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -60,10 +60,12 @@ export function EditableArtifactMessage({
|
|
|
60
60
|
title,
|
|
61
61
|
detail,
|
|
62
62
|
retry,
|
|
63
|
+
retryLabel = "Retry",
|
|
63
64
|
}: {
|
|
64
65
|
title: string;
|
|
65
66
|
detail: string;
|
|
66
67
|
retry?: (() => void) | undefined;
|
|
68
|
+
retryLabel?: string | undefined;
|
|
67
69
|
}) {
|
|
68
70
|
return (
|
|
69
71
|
<div className="grid h-full min-h-56 place-items-center bg-og-bg p-6 text-center">
|
|
@@ -76,7 +78,7 @@ export function EditableArtifactMessage({
|
|
|
76
78
|
onClick={retry}
|
|
77
79
|
className="mt-3 rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-3 py-1.5 text-og-sm font-medium hover:bg-og-primary-hover outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent"
|
|
78
80
|
>
|
|
79
|
-
|
|
81
|
+
{retryLabel}
|
|
80
82
|
</button>
|
|
81
83
|
) : null}
|
|
82
84
|
</div>
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import type { EditableArtifactResource } from "@opengeni/sdk/artifacts";
|
|
2
|
+
import {
|
|
3
|
+
createBrowserEditableArtifactSession,
|
|
4
|
+
type CreateBrowserEditableArtifactSessionOptions,
|
|
5
|
+
type EditableArtifactBrowserRuntime,
|
|
6
|
+
type EditableArtifactCacheAuthority,
|
|
7
|
+
type EditableArtifactModality,
|
|
8
|
+
} from "@opengeni/sdk/editable-artifacts";
|
|
9
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
ArtifactLoading,
|
|
13
|
+
ArtifactProblem,
|
|
14
|
+
artifactLoadErrorView,
|
|
15
|
+
useArtifactLabels,
|
|
16
|
+
type ArtifactLoadErrorView,
|
|
17
|
+
} from "./artifact-chrome";
|
|
18
|
+
import { EditableArtifactWorkbenchHost } from "./editable-artifact-workbench";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The browser kernels an editor needs, one per modality, plus the host-built
|
|
22
|
+
* Worker URL (for Vite: `@opengeni/sdk/editable-artifacts/worker?worker&url`).
|
|
23
|
+
* Kernels come from `@opengeni/artifact-kernel-wasm-{document,spreadsheet,presentation}`.
|
|
24
|
+
*/
|
|
25
|
+
export type EditableArtifactRuntimes = Readonly<{
|
|
26
|
+
document: Omit<EditableArtifactBrowserRuntime, "workerUrl" | "workerFactory">;
|
|
27
|
+
spreadsheet: Omit<EditableArtifactBrowserRuntime, "workerUrl" | "workerFactory">;
|
|
28
|
+
presentation: Omit<EditableArtifactBrowserRuntime, "workerUrl" | "workerFactory">;
|
|
29
|
+
workerUrl: string;
|
|
30
|
+
applicationOrigin?: string | undefined;
|
|
31
|
+
/** Loopback development only. */
|
|
32
|
+
allowInsecureDevelopmentAssets?: boolean | undefined;
|
|
33
|
+
}>;
|
|
34
|
+
|
|
35
|
+
/** Everything needed to open one artifact for the current principal. */
|
|
36
|
+
export type OpenedEditableArtifact = Readonly<{
|
|
37
|
+
artifact: EditableArtifactResource;
|
|
38
|
+
authority: EditableArtifactCacheAuthority;
|
|
39
|
+
replicaId: string;
|
|
40
|
+
}>;
|
|
41
|
+
|
|
42
|
+
export type EditableArtifactViewProps = Readonly<{
|
|
43
|
+
/** Absolute API (or session proxy) base the live transport talks to. */
|
|
44
|
+
baseUrl: URL;
|
|
45
|
+
workspaceId: string;
|
|
46
|
+
artifactId: string;
|
|
47
|
+
/** Resolve metadata, cache authority, and replica; called again on retry. */
|
|
48
|
+
open: (signal: AbortSignal) => Promise<OpenedEditableArtifact>;
|
|
49
|
+
runtimes: EditableArtifactRuntimes;
|
|
50
|
+
transport?: CreateBrowserEditableArtifactSessionOptions["transport"];
|
|
51
|
+
/** Show the editor's own title header (hosts with their own header hide it). */
|
|
52
|
+
showHeader?: boolean | undefined;
|
|
53
|
+
/** Re-open when this changes (for example the host's credential version). */
|
|
54
|
+
authorityKey?: string | undefined;
|
|
55
|
+
describeError?: ((error: unknown) => ArtifactLoadErrorView) | undefined;
|
|
56
|
+
onTitle?: ((title: string) => void) | undefined;
|
|
57
|
+
/** Loading copy; defaults to the `opening` label. */
|
|
58
|
+
loadingLabel?: string | undefined;
|
|
59
|
+
}>;
|
|
60
|
+
|
|
61
|
+
type LoadState =
|
|
62
|
+
| Readonly<{ key: string | null; kind: "loading" }>
|
|
63
|
+
| Readonly<{ key: string; kind: "ready"; value: OpenedEditableArtifact }>
|
|
64
|
+
| Readonly<{ key: string; kind: "error"; error: unknown }>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* One editable document, spreadsheet, or presentation with its first-party
|
|
68
|
+
* editor. The console dock/page and embedding hosts supply only data access.
|
|
69
|
+
*/
|
|
70
|
+
export function EditableArtifactView({
|
|
71
|
+
baseUrl,
|
|
72
|
+
workspaceId,
|
|
73
|
+
artifactId,
|
|
74
|
+
open,
|
|
75
|
+
runtimes,
|
|
76
|
+
transport,
|
|
77
|
+
showHeader = true,
|
|
78
|
+
authorityKey,
|
|
79
|
+
describeError,
|
|
80
|
+
onTitle,
|
|
81
|
+
loadingLabel,
|
|
82
|
+
}: EditableArtifactViewProps) {
|
|
83
|
+
const [loadEpoch, setLoadEpoch] = useState(0);
|
|
84
|
+
const labels = useArtifactLabels();
|
|
85
|
+
const loadKey = JSON.stringify([artifactId, authorityKey, baseUrl.href, workspaceId, loadEpoch]);
|
|
86
|
+
const [state, setState] = useState<LoadState>({ key: null, kind: "loading" });
|
|
87
|
+
const current = state.key === loadKey ? state : null;
|
|
88
|
+
const openRef = useRef(open);
|
|
89
|
+
openRef.current = open;
|
|
90
|
+
const onTitleRef = useRef(onTitle);
|
|
91
|
+
onTitleRef.current = onTitle;
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const abort = new AbortController();
|
|
94
|
+
setState({ key: loadKey, kind: "loading" });
|
|
95
|
+
void openRef
|
|
96
|
+
.current(abort.signal)
|
|
97
|
+
.then((value) => {
|
|
98
|
+
if (abort.signal.aborted) return;
|
|
99
|
+
setState({ key: loadKey, kind: "ready", value });
|
|
100
|
+
onTitleRef.current?.(value.artifact.title);
|
|
101
|
+
})
|
|
102
|
+
.catch((error: unknown) => {
|
|
103
|
+
if (!abort.signal.aborted) setState({ key: loadKey, kind: "error", error });
|
|
104
|
+
});
|
|
105
|
+
return () => abort.abort();
|
|
106
|
+
}, [loadKey]);
|
|
107
|
+
|
|
108
|
+
const ready = current?.kind === "ready" ? current.value : null;
|
|
109
|
+
const transportRef = useRef(transport);
|
|
110
|
+
transportRef.current = transport;
|
|
111
|
+
const sessionKey = useMemo(
|
|
112
|
+
() =>
|
|
113
|
+
ready
|
|
114
|
+
? JSON.stringify({
|
|
115
|
+
baseUrl: baseUrl.href,
|
|
116
|
+
authorityKey,
|
|
117
|
+
workspaceId,
|
|
118
|
+
artifact: { id: ready.artifact.id, modality: ready.artifact.modality },
|
|
119
|
+
authority: ready.authority,
|
|
120
|
+
replicaId: ready.replicaId,
|
|
121
|
+
workerUrl: runtimes.workerUrl,
|
|
122
|
+
})
|
|
123
|
+
: "",
|
|
124
|
+
[authorityKey, baseUrl.href, ready, runtimes.workerUrl, workspaceId],
|
|
125
|
+
);
|
|
126
|
+
const createSession = useCallback(() => {
|
|
127
|
+
if (!ready) throw new Error("Artifact is not open");
|
|
128
|
+
return createBrowserEditableArtifactSession({
|
|
129
|
+
baseUrl,
|
|
130
|
+
workspaceId,
|
|
131
|
+
artifact: ready.artifact,
|
|
132
|
+
storageAuthority: ready.authority,
|
|
133
|
+
replicaId: ready.replicaId,
|
|
134
|
+
runtime: {
|
|
135
|
+
...runtimeFor(runtimes, ready.artifact.modality),
|
|
136
|
+
workerUrl: runtimes.workerUrl,
|
|
137
|
+
...(runtimes.applicationOrigin ? { applicationOrigin: runtimes.applicationOrigin } : {}),
|
|
138
|
+
...(runtimes.allowInsecureDevelopmentAssets
|
|
139
|
+
? { allowInsecureDevelopmentAssets: true }
|
|
140
|
+
: {}),
|
|
141
|
+
},
|
|
142
|
+
...(transportRef.current ? { transport: transportRef.current } : {}),
|
|
143
|
+
});
|
|
144
|
+
}, [baseUrl, ready, runtimes, workspaceId]);
|
|
145
|
+
|
|
146
|
+
if (!current || current.kind === "loading")
|
|
147
|
+
return <ArtifactLoading label={loadingLabel ?? labels.opening} />;
|
|
148
|
+
if (current.kind === "error") {
|
|
149
|
+
return (
|
|
150
|
+
<ArtifactProblem
|
|
151
|
+
view={
|
|
152
|
+
describeError
|
|
153
|
+
? describeError(current.error)
|
|
154
|
+
: artifactLoadErrorView(current.error, "editable", labels)
|
|
155
|
+
}
|
|
156
|
+
onRetry={() => setLoadEpoch((value) => value + 1)}
|
|
157
|
+
/>
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
const { artifact } = current.value;
|
|
161
|
+
const surface = { title: artifact.title, showHeader };
|
|
162
|
+
return (
|
|
163
|
+
<div className="h-full min-h-0 bg-og-bg text-og-fg">
|
|
164
|
+
<EditableArtifactWorkbenchHost
|
|
165
|
+
sessionKey={sessionKey}
|
|
166
|
+
createSession={createSession}
|
|
167
|
+
document={surface}
|
|
168
|
+
spreadsheet={surface}
|
|
169
|
+
presentation={surface}
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function runtimeFor(
|
|
176
|
+
runtimes: EditableArtifactRuntimes,
|
|
177
|
+
modality: EditableArtifactModality,
|
|
178
|
+
): Omit<EditableArtifactBrowserRuntime, "workerUrl" | "workerFactory"> {
|
|
179
|
+
switch (modality) {
|
|
180
|
+
case "document":
|
|
181
|
+
return runtimes.document;
|
|
182
|
+
case "spreadsheet":
|
|
183
|
+
return runtimes.spreadsheet;
|
|
184
|
+
case "presentation":
|
|
185
|
+
return runtimes.presentation;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
} from "@opengeni/sdk/editable-artifacts";
|
|
7
7
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
8
8
|
|
|
9
|
+
import { useArtifactLabels } from "./artifact-chrome";
|
|
9
10
|
import { EditableArtifactMessage } from "./editable-artifact-ui";
|
|
10
11
|
import {
|
|
11
12
|
EditableDocumentArtifactSurface,
|
|
@@ -133,6 +134,7 @@ export function EditableArtifactWorkbenchHost({
|
|
|
133
134
|
}, [retryEpoch, sessionKey]);
|
|
134
135
|
|
|
135
136
|
const retry = useCallback(() => setRetryEpoch((value) => value + 1), []);
|
|
137
|
+
const labels = useArtifactLabels();
|
|
136
138
|
const currentError = state.key === sessionKey ? state.error : null;
|
|
137
139
|
if (state.key === sessionKey && state.session) {
|
|
138
140
|
return (
|
|
@@ -146,9 +148,10 @@ export function EditableArtifactWorkbenchHost({
|
|
|
146
148
|
}
|
|
147
149
|
return (
|
|
148
150
|
<EditableArtifactMessage
|
|
149
|
-
title={currentError ?
|
|
150
|
-
detail={currentError?.message ??
|
|
151
|
+
title={currentError ? labels.editableErrors.transient.title : labels.opening}
|
|
152
|
+
detail={currentError?.message ?? labels.startingEditor}
|
|
151
153
|
retry={currentError ? retry : undefined}
|
|
154
|
+
retryLabel={labels.retry}
|
|
152
155
|
/>
|
|
153
156
|
);
|
|
154
157
|
}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
type EditableArtifactSession,
|
|
10
10
|
} from "@opengeni/sdk/editable-artifacts";
|
|
11
11
|
import { type ReactNode, useCallback, useEffect, useRef, useState } from "react";
|
|
12
|
+
import { useLatestTaskRunner } from "./latest-task-runner";
|
|
12
13
|
|
|
13
14
|
import {
|
|
14
15
|
DocumentProjectionArtifactSurface,
|
|
@@ -87,6 +88,7 @@ export function EditableDocumentArtifactSurface({
|
|
|
87
88
|
error: null,
|
|
88
89
|
});
|
|
89
90
|
const loadGeneration = useRef(0);
|
|
91
|
+
const scheduleLoad = useLatestTaskRunner(session);
|
|
90
92
|
const commandStateRef = useRef<DocumentCommandState | null>(null);
|
|
91
93
|
if (commandStateRef.current?.session !== session) {
|
|
92
94
|
commandStateRef.current = {
|
|
@@ -107,45 +109,41 @@ export function EditableDocumentArtifactSurface({
|
|
|
107
109
|
? { ...current, loading: true, error: null }
|
|
108
110
|
: { session, projection: null, loading: true, error: null },
|
|
109
111
|
);
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
error: asEditableArtifactError(cause, "Could not open this document"),
|
|
142
|
-
});
|
|
143
|
-
},
|
|
144
|
-
);
|
|
112
|
+
const superseded = () =>
|
|
113
|
+
cancelled ||
|
|
114
|
+
generation !== loadGeneration.current ||
|
|
115
|
+
commandStateRef.current !== commandState;
|
|
116
|
+
scheduleLoad(async () => {
|
|
117
|
+
if (superseded()) return;
|
|
118
|
+
await composeDocumentEditorProjection(session).then(
|
|
119
|
+
(projection) => {
|
|
120
|
+
if (superseded()) return;
|
|
121
|
+
const nextParagraphText = paragraphTextById(projection);
|
|
122
|
+
for (const [localId, canonicalId] of commandState.optimisticParagraphIds) {
|
|
123
|
+
if (nextParagraphText.has(canonicalId)) continue;
|
|
124
|
+
const optimisticText =
|
|
125
|
+
commandState.paragraphText.get(canonicalId) ??
|
|
126
|
+
commandState.paragraphText.get(localId);
|
|
127
|
+
if (optimisticText !== undefined) nextParagraphText.set(canonicalId, optimisticText);
|
|
128
|
+
}
|
|
129
|
+
commandState.paragraphText = nextParagraphText;
|
|
130
|
+
setState({ session, projection, loading: false, error: null });
|
|
131
|
+
},
|
|
132
|
+
(cause) => {
|
|
133
|
+
if (superseded()) return;
|
|
134
|
+
setState({
|
|
135
|
+
session,
|
|
136
|
+
projection: null,
|
|
137
|
+
loading: false,
|
|
138
|
+
error: asEditableArtifactError(cause, "Could not open this document"),
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
);
|
|
142
|
+
});
|
|
145
143
|
return () => {
|
|
146
144
|
cancelled = true;
|
|
147
145
|
};
|
|
148
|
-
}, [commandState, invalidator, retryEpoch, session]);
|
|
146
|
+
}, [commandState, invalidator, retryEpoch, scheduleLoad, session]);
|
|
149
147
|
|
|
150
148
|
const refresh = useCallback(() => setRetryEpoch((value) => value + 1), []);
|
|
151
149
|
const writable = !readOnly && view.writable;
|