@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
package/dist/sites.js
CHANGED
|
@@ -1,293 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import { useEffect, useRef, useState } from "react";
|
|
7
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
8
|
-
async function loadSiteSnapshot(client, workspaceId, siteId, options = {}) {
|
|
9
|
-
const requestOptions = options.signal ? { signal: options.signal } : {};
|
|
10
|
-
const detail = await client.getWorkspaceArtifact(workspaceId, siteId, requestOptions);
|
|
11
|
-
options.signal?.throwIfAborted();
|
|
12
|
-
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
13
|
-
throw new Error("Site scope mismatch");
|
|
14
|
-
const selectedVersion = options.versionId ? detail.versions.find((version) => version.id === options.versionId) : detail.artifact.currentVersion;
|
|
15
|
-
const versionId = options.versionId ?? selectedVersion?.id;
|
|
16
|
-
let content = selectedVersion && versionId && (detail.artifact.status === "active" || options.includeArchivedContent) ? {
|
|
17
|
-
artifactId: siteId,
|
|
18
|
-
versionId,
|
|
19
|
-
requestedTools: selectedVersion.requestedTools,
|
|
20
|
-
html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {
|
|
21
|
-
...requestOptions,
|
|
22
|
-
versionId
|
|
23
|
-
})
|
|
24
|
-
} : null;
|
|
25
|
-
if (versionId && !selectedVersion && (detail.artifact.status === "active" || options.includeArchivedContent)) {
|
|
26
|
-
if (!client.getWorkspaceArtifactContent) throw new Error("Site version unavailable");
|
|
27
|
-
content = await client.getWorkspaceArtifactContent(workspaceId, siteId, {
|
|
28
|
-
...requestOptions,
|
|
29
|
-
versionId
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
options.signal?.throwIfAborted();
|
|
33
|
-
if (content && (content.artifactId !== siteId || content.versionId !== versionId))
|
|
34
|
-
throw new Error("Site content mismatch");
|
|
35
|
-
return { detail: structuredClone(detail), content: content ? structuredClone(content) : null };
|
|
36
|
-
}
|
|
37
|
-
function useScopeKey(values) {
|
|
38
|
-
const [scope, setScope] = useState(values);
|
|
39
|
-
const [generation, setGeneration] = useState(0);
|
|
40
|
-
if (values.some((value, index) => value !== scope[index])) {
|
|
41
|
-
setScope(values);
|
|
42
|
-
setGeneration(generation + 1);
|
|
43
|
-
}
|
|
44
|
-
return generation;
|
|
45
|
-
}
|
|
46
|
-
function SiteList(props) {
|
|
47
|
-
const key = useScopeKey([props.client, props.workspaceId, props.status]);
|
|
48
|
-
return /* @__PURE__ */ jsx(ScopedSiteList, { ...props }, key);
|
|
49
|
-
}
|
|
50
|
-
function ScopedSiteList({
|
|
51
|
-
client,
|
|
52
|
-
workspaceId,
|
|
53
|
-
status = "active",
|
|
54
|
-
onOpen,
|
|
55
|
-
className
|
|
56
|
-
}) {
|
|
57
|
-
const [page, setPage] = useState(null);
|
|
58
|
-
const [cursor, setCursor] = useState();
|
|
59
|
-
const [error, setError] = useState(false);
|
|
60
|
-
const [reload, setReload] = useState(0);
|
|
61
|
-
useEffect(() => {
|
|
62
|
-
const abort = new AbortController();
|
|
63
|
-
setPage(null);
|
|
64
|
-
setError(false);
|
|
65
|
-
void Promise.resolve().then(
|
|
66
|
-
() => client.listWorkspaceArtifacts(workspaceId, {
|
|
67
|
-
status,
|
|
68
|
-
...cursor ? { cursor } : {},
|
|
69
|
-
limit: 50,
|
|
70
|
-
signal: abort.signal
|
|
71
|
-
})
|
|
72
|
-
).then((result) => {
|
|
73
|
-
if (!abort.signal.aborted) setPage(structuredClone(result));
|
|
74
|
-
}).catch(() => {
|
|
75
|
-
if (!abort.signal.aborted) setError(true);
|
|
76
|
-
});
|
|
77
|
-
return () => abort.abort();
|
|
78
|
-
}, [client, workspaceId, status, cursor, reload]);
|
|
79
|
-
return /* @__PURE__ */ jsxs("section", { className, "aria-label": "Sites", "aria-busy": !page && !error, children: [
|
|
80
|
-
error ? /* @__PURE__ */ jsx("p", { role: "alert", children: "Sites are unavailable or access has changed. Refresh to check access." }) : !page ? /* @__PURE__ */ jsx("p", { role: "status", children: "Loading Sites\u2026" }) : page.artifacts.length === 0 ? /* @__PURE__ */ jsxs("p", { role: "status", children: [
|
|
81
|
-
"No ",
|
|
82
|
-
status,
|
|
83
|
-
" Sites."
|
|
84
|
-
] }) : /* @__PURE__ */ jsx("ul", { children: page.artifacts.map((site) => /* @__PURE__ */ jsxs("li", { children: [
|
|
85
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => onOpen(structuredClone(site)), children: site.title }),
|
|
86
|
-
/* @__PURE__ */ jsx("p", { children: site.description }),
|
|
87
|
-
/* @__PURE__ */ jsxs("span", { children: [
|
|
88
|
-
site.status,
|
|
89
|
-
" \xB7 revision ",
|
|
90
|
-
site.currentVersion?.revision ?? "unpublished"
|
|
91
|
-
] })
|
|
92
|
-
] }, site.id)) }),
|
|
93
|
-
page?.nextCursor && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCursor(page.nextCursor), children: "Next page" }),
|
|
94
|
-
cursor && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCursor(void 0), children: "First page" }),
|
|
95
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setReload((value) => value + 1), children: "Refresh Sites" })
|
|
96
|
-
] });
|
|
97
|
-
}
|
|
98
|
-
function SiteDetail(props) {
|
|
99
|
-
const key = useScopeKey([props.client, props.workspaceId, props.siteId]);
|
|
100
|
-
return /* @__PURE__ */ jsx(ScopedSiteDetail, { ...props }, key);
|
|
101
|
-
}
|
|
102
|
-
function ScopedSiteDetail({
|
|
103
|
-
client,
|
|
104
|
-
workspaceId,
|
|
105
|
-
siteId,
|
|
106
|
-
canPublish = false,
|
|
107
|
-
toolBridge,
|
|
108
|
-
className
|
|
109
|
-
}) {
|
|
110
|
-
const [loaded, setLoaded] = useState(null);
|
|
111
|
-
const [error, setError] = useState(false);
|
|
112
|
-
const [reload, setReload] = useState(0);
|
|
113
|
-
const [busy, setBusy] = useState(false);
|
|
114
|
-
const [rollbackId, setRollbackId] = useState("");
|
|
115
|
-
const [confirmation, setConfirmation] = useState(null);
|
|
116
|
-
const mutation = useRef(null);
|
|
117
|
-
useEffect(() => () => mutation.current?.abort(), []);
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
const abort = new AbortController();
|
|
120
|
-
setLoaded(null);
|
|
121
|
-
setError(false);
|
|
122
|
-
setConfirmation(null);
|
|
123
|
-
setRollbackId("");
|
|
124
|
-
void Promise.resolve().then(async () => {
|
|
125
|
-
const snapshot = await loadSiteSnapshot(client, workspaceId, siteId, {
|
|
126
|
-
signal: abort.signal
|
|
127
|
-
});
|
|
128
|
-
if (!abort.signal.aborted) setLoaded(snapshot);
|
|
129
|
-
}).catch(() => {
|
|
130
|
-
if (!abort.signal.aborted) {
|
|
131
|
-
setLoaded(null);
|
|
132
|
-
setError(true);
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
return () => abort.abort();
|
|
136
|
-
}, [client, workspaceId, siteId, reload]);
|
|
137
|
-
useEffect(() => {
|
|
138
|
-
if (!loaded || busy) return;
|
|
139
|
-
const abort = new AbortController();
|
|
140
|
-
let checking = false;
|
|
141
|
-
const timer = setInterval(() => {
|
|
142
|
-
if (checking) return;
|
|
143
|
-
checking = true;
|
|
144
|
-
void Promise.resolve().then(() => client.getWorkspaceArtifact(workspaceId, siteId, { signal: abort.signal })).then((detail) => {
|
|
145
|
-
if (abort.signal.aborted) return;
|
|
146
|
-
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
147
|
-
throw new Error("Site scope mismatch");
|
|
148
|
-
if (detail.artifact.currentVersion?.id !== loaded.detail.artifact.currentVersion?.id || detail.artifact.status !== loaded.detail.artifact.status) {
|
|
149
|
-
setLoaded(null);
|
|
150
|
-
setReload((value) => value + 1);
|
|
151
|
-
}
|
|
152
|
-
}).catch(() => {
|
|
153
|
-
if (!abort.signal.aborted) {
|
|
154
|
-
setLoaded(null);
|
|
155
|
-
setError(true);
|
|
156
|
-
}
|
|
157
|
-
}).finally(() => {
|
|
158
|
-
checking = false;
|
|
159
|
-
});
|
|
160
|
-
}, 15e3);
|
|
161
|
-
return () => {
|
|
162
|
-
abort.abort();
|
|
163
|
-
clearInterval(timer);
|
|
164
|
-
};
|
|
165
|
-
}, [client, workspaceId, siteId, loaded, busy]);
|
|
166
|
-
const commit = async () => {
|
|
167
|
-
const current = loaded?.detail.artifact.currentVersion;
|
|
168
|
-
if (!canPublish || !loaded || !current || !confirmation || mutation.current) return;
|
|
169
|
-
const abort = new AbortController();
|
|
170
|
-
mutation.current = abort;
|
|
171
|
-
setBusy(true);
|
|
172
|
-
try {
|
|
173
|
-
const common = { expectedCurrentVersionId: current.id, idempotencyKey: crypto.randomUUID() };
|
|
174
|
-
if (confirmation === "rollback") {
|
|
175
|
-
if (!loaded.detail.versions.some((version) => version.id === rollbackId))
|
|
176
|
-
throw new Error("Unknown version");
|
|
177
|
-
await client.rollbackWorkspaceArtifact(
|
|
178
|
-
workspaceId,
|
|
179
|
-
siteId,
|
|
180
|
-
{ ...common, versionId: rollbackId, reason: "Restore explicitly selected Site version" },
|
|
181
|
-
{ signal: abort.signal }
|
|
182
|
-
);
|
|
183
|
-
} else {
|
|
184
|
-
await client.setWorkspaceArtifactStatus(
|
|
185
|
-
workspaceId,
|
|
186
|
-
siteId,
|
|
187
|
-
{
|
|
188
|
-
...common,
|
|
189
|
-
status: loaded.detail.artifact.status === "active" ? "archived" : "active",
|
|
190
|
-
reason: "Explicit Site status change"
|
|
191
|
-
},
|
|
192
|
-
{ signal: abort.signal }
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
if (!abort.signal.aborted) {
|
|
196
|
-
setLoaded(null);
|
|
197
|
-
setReload((value) => value + 1);
|
|
198
|
-
}
|
|
199
|
-
} catch {
|
|
200
|
-
if (!abort.signal.aborted) {
|
|
201
|
-
setLoaded(null);
|
|
202
|
-
setError(true);
|
|
203
|
-
}
|
|
204
|
-
} finally {
|
|
205
|
-
if (!abort.signal.aborted) {
|
|
206
|
-
mutation.current = null;
|
|
207
|
-
setBusy(false);
|
|
208
|
-
setConfirmation(null);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
};
|
|
212
|
-
const site = loaded?.detail.artifact;
|
|
213
|
-
return /* @__PURE__ */ jsxs(
|
|
214
|
-
"section",
|
|
215
|
-
{
|
|
216
|
-
className,
|
|
217
|
-
"aria-label": "Site details",
|
|
218
|
-
"aria-busy": busy || !loaded && !error,
|
|
219
|
-
children: [
|
|
220
|
-
error && /* @__PURE__ */ jsx("p", { role: "alert", children: "Site state could not be confirmed or access has changed. Refresh before continuing." }),
|
|
221
|
-
!loaded && !error && /* @__PURE__ */ jsx("p", { role: "status", children: "Loading Site\u2026" }),
|
|
222
|
-
site && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
223
|
-
/* @__PURE__ */ jsx("h2", { children: site.title }),
|
|
224
|
-
/* @__PURE__ */ jsx("p", { children: site.description }),
|
|
225
|
-
/* @__PURE__ */ jsxs("p", { children: [
|
|
226
|
-
site.status,
|
|
227
|
-
" \xB7 revision ",
|
|
228
|
-
site.currentVersion?.revision ?? "unpublished"
|
|
229
|
-
] }),
|
|
230
|
-
loaded.content && /* @__PURE__ */ jsx(
|
|
231
|
-
PublishedHtmlArtifactFrame,
|
|
232
|
-
{
|
|
233
|
-
html: loaded.content.html,
|
|
234
|
-
title: site.title,
|
|
235
|
-
...toolBridge ? { toolBridge } : {}
|
|
236
|
-
}
|
|
237
|
-
),
|
|
238
|
-
/* @__PURE__ */ jsx("h3", { children: "Versions" }),
|
|
239
|
-
/* @__PURE__ */ jsx("ul", { children: loaded.detail.versions.map((version) => /* @__PURE__ */ jsxs("li", { children: [
|
|
240
|
-
"Revision ",
|
|
241
|
-
version.revision,
|
|
242
|
-
" \xB7 ",
|
|
243
|
-
version.createdAt,
|
|
244
|
-
version.id === site.currentVersion?.id ? " \xB7 current" : ""
|
|
245
|
-
] }, version.id)) }),
|
|
246
|
-
loaded.detail.versionsTruncated && /* @__PURE__ */ jsx("p", { children: "Older versions are not included in this response." }),
|
|
247
|
-
canPublish && site.currentVersion && /* @__PURE__ */ jsxs("fieldset", { disabled: busy, children: [
|
|
248
|
-
/* @__PURE__ */ jsx("legend", { children: "Manage Site" }),
|
|
249
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmation("status"), children: site.status === "active" ? "Archive Site" : "Restore Site" }),
|
|
250
|
-
/* @__PURE__ */ jsxs("label", { children: [
|
|
251
|
-
"Version to restore",
|
|
252
|
-
/* @__PURE__ */ jsxs(
|
|
253
|
-
"select",
|
|
254
|
-
{
|
|
255
|
-
value: rollbackId,
|
|
256
|
-
onChange: (event) => {
|
|
257
|
-
setRollbackId(event.target.value);
|
|
258
|
-
setConfirmation(null);
|
|
259
|
-
},
|
|
260
|
-
children: [
|
|
261
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Choose a version" }),
|
|
262
|
-
loaded.detail.versions.filter((version) => version.id !== site.currentVersion?.id).map((version) => /* @__PURE__ */ jsxs("option", { value: version.id, children: [
|
|
263
|
-
"Revision ",
|
|
264
|
-
version.revision
|
|
265
|
-
] }, version.id))
|
|
266
|
-
]
|
|
267
|
-
}
|
|
268
|
-
)
|
|
269
|
-
] }),
|
|
270
|
-
/* @__PURE__ */ jsx(
|
|
271
|
-
"button",
|
|
272
|
-
{
|
|
273
|
-
type: "button",
|
|
274
|
-
disabled: !rollbackId,
|
|
275
|
-
onClick: () => setConfirmation("rollback"),
|
|
276
|
-
children: "Review rollback"
|
|
277
|
-
}
|
|
278
|
-
),
|
|
279
|
-
confirmation && /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Confirm Site change", children: [
|
|
280
|
-
/* @__PURE__ */ jsx("p", { children: confirmation === "rollback" ? "Make the selected version current?" : site.status === "active" ? "Archive this Site for workspace users?" : "Restore this Site for workspace users?" }),
|
|
281
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void commit(), children: "Confirm change" }),
|
|
282
|
-
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmation(null), children: "Keep current state" })
|
|
283
|
-
] })
|
|
284
|
-
] })
|
|
285
|
-
] }),
|
|
286
|
-
/* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => setReload((value) => value + 1), children: "Refresh Site" })
|
|
287
|
-
]
|
|
288
|
-
}
|
|
289
|
-
);
|
|
290
|
-
}
|
|
2
|
+
SiteDetail,
|
|
3
|
+
SiteList,
|
|
4
|
+
loadSiteSnapshot
|
|
5
|
+
} from "./chunk-SCOTPE2I.js";
|
|
291
6
|
export {
|
|
292
7
|
SiteDetail,
|
|
293
8
|
SiteList,
|
package/dist/sites.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sites-ui.tsx"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\nimport type {\n OpenGeniClient,\n WorkspaceArtifact,\n WorkspaceArtifactContentResponse,\n WorkspaceArtifactDetailResponse,\n WorkspaceArtifactListResponse,\n} from \"@opengeni/sdk\";\nimport {\n PublishedHtmlArtifactFrame,\n type PublishedHtmlArtifactToolBridge,\n} from \"./components/artifacts/published-html-artifact-frame\";\n\n/** Implement with an authenticated host proxy. Never put an organization key\n * in browser props; replace the client object when the host actor changes. */\nexport type SiteClient = Pick<\n OpenGeniClient,\n | \"listWorkspaceArtifacts\"\n | \"getWorkspaceArtifact\"\n | \"getWorkspaceArtifactHtml\"\n | \"rollbackWorkspaceArtifact\"\n | \"setWorkspaceArtifactStatus\"\n>;\ntype SiteDisplayContent = Pick<\n WorkspaceArtifactContentResponse,\n \"artifactId\" | \"versionId\" | \"html\" | \"requestedTools\"\n>;\ntype SiteScope = { client: SiteClient; workspaceId: string; className?: string };\n/** Shared native/embedded read boundary: pin content to the observed version,\n * and never accept a response belonging to another workspace or Site. */\nexport async function loadSiteSnapshot(\n client: Pick<SiteClient, \"getWorkspaceArtifact\" | \"getWorkspaceArtifactHtml\"> &\n Partial<Pick<OpenGeniClient, \"getWorkspaceArtifactContent\">>,\n workspaceId: string,\n siteId: string,\n options: { signal?: AbortSignal; includeArchivedContent?: boolean; versionId?: string } = {},\n): Promise<{\n detail: WorkspaceArtifactDetailResponse;\n content: SiteDisplayContent | null;\n}> {\n const requestOptions = options.signal ? { signal: options.signal } : {};\n const detail = await client.getWorkspaceArtifact(workspaceId, siteId, requestOptions);\n options.signal?.throwIfAborted();\n if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)\n throw new Error(\"Site scope mismatch\");\n const selectedVersion = options.versionId\n ? detail.versions.find((version) => version.id === options.versionId)\n : detail.artifact.currentVersion;\n const versionId = options.versionId ?? selectedVersion?.id;\n let content: SiteDisplayContent | null =\n selectedVersion &&\n versionId &&\n (detail.artifact.status === \"active\" || options.includeArchivedContent)\n ? {\n artifactId: siteId,\n versionId,\n requestedTools: selectedVersion!.requestedTools,\n html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {\n ...requestOptions,\n versionId,\n }),\n }\n : null;\n if (\n versionId &&\n !selectedVersion &&\n (detail.artifact.status === \"active\" || options.includeArchivedContent)\n ) {\n if (!client.getWorkspaceArtifactContent) throw new Error(\"Site version unavailable\");\n content = await client.getWorkspaceArtifactContent(workspaceId, siteId, {\n ...requestOptions,\n versionId,\n });\n }\n options.signal?.throwIfAborted();\n if (content && (content.artifactId !== siteId || content.versionId !== versionId))\n throw new Error(\"Site content mismatch\");\n return { detail: structuredClone(detail), content: content ? structuredClone(content) : null };\n}\nexport type SiteListProps = SiteScope & {\n status?: \"active\" | \"archived\";\n onOpen: (site: WorkspaceArtifact) => void;\n};\nexport type SiteDetailProps = SiteScope & {\n siteId: string;\n /** Presentation hint only: the backend still enforces artifacts:publish. */\n canPublish?: boolean;\n toolBridge?: PublishedHtmlArtifactToolBridge;\n};\n\nfunction useScopeKey(values: readonly unknown[]) {\n const [scope, setScope] = useState(values);\n const [generation, setGeneration] = useState(0);\n if (values.some((value, index) => value !== scope[index])) {\n setScope(values);\n setGeneration(generation + 1);\n }\n return generation;\n}\n\nexport function SiteList(props: SiteListProps) {\n const key = useScopeKey([props.client, props.workspaceId, props.status]);\n return <ScopedSiteList key={key} {...props} />;\n}\nfunction ScopedSiteList({\n client,\n workspaceId,\n status = \"active\",\n onOpen,\n className,\n}: SiteListProps) {\n const [page, setPage] = useState<WorkspaceArtifactListResponse | null>(null);\n const [cursor, setCursor] = useState<string | undefined>();\n const [error, setError] = useState(false);\n const [reload, setReload] = useState(0);\n useEffect(() => {\n const abort = new AbortController();\n setPage(null);\n setError(false);\n void Promise.resolve()\n .then(() =>\n client.listWorkspaceArtifacts(workspaceId, {\n status,\n ...(cursor ? { cursor } : {}),\n limit: 50,\n signal: abort.signal,\n }),\n )\n .then((result) => {\n if (!abort.signal.aborted) setPage(structuredClone(result));\n })\n .catch(() => {\n if (!abort.signal.aborted) setError(true);\n });\n return () => abort.abort();\n }, [client, workspaceId, status, cursor, reload]);\n return (\n <section className={className} aria-label=\"Sites\" aria-busy={!page && !error}>\n {error ? (\n <p role=\"alert\">Sites are unavailable or access has changed. Refresh to check access.</p>\n ) : !page ? (\n <p role=\"status\">Loading Sites…</p>\n ) : page.artifacts.length === 0 ? (\n <p role=\"status\">No {status} Sites.</p>\n ) : (\n <ul>\n {page.artifacts.map((site) => (\n <li key={site.id}>\n <button type=\"button\" onClick={() => onOpen(structuredClone(site))}>\n {site.title}\n </button>\n <p>{site.description}</p>\n <span>\n {site.status} · revision {site.currentVersion?.revision ?? \"unpublished\"}\n </span>\n </li>\n ))}\n </ul>\n )}\n {page?.nextCursor && (\n <button type=\"button\" onClick={() => setCursor(page.nextCursor!)}>\n Next page\n </button>\n )}\n {cursor && (\n <button type=\"button\" onClick={() => setCursor(undefined)}>\n First page\n </button>\n )}\n <button type=\"button\" onClick={() => setReload((value) => value + 1)}>\n Refresh Sites\n </button>\n </section>\n );\n}\n\nexport function SiteDetail(props: SiteDetailProps) {\n const key = useScopeKey([props.client, props.workspaceId, props.siteId]);\n return <ScopedSiteDetail key={key} {...props} />;\n}\nfunction ScopedSiteDetail({\n client,\n workspaceId,\n siteId,\n canPublish = false,\n toolBridge,\n className,\n}: SiteDetailProps) {\n const [loaded, setLoaded] = useState<{\n detail: WorkspaceArtifactDetailResponse;\n content: SiteDisplayContent | null;\n } | null>(null);\n const [error, setError] = useState(false);\n const [reload, setReload] = useState(0);\n const [busy, setBusy] = useState(false);\n const [rollbackId, setRollbackId] = useState(\"\");\n const [confirmation, setConfirmation] = useState<\"status\" | \"rollback\" | null>(null);\n const mutation = useRef<AbortController | null>(null);\n useEffect(() => () => mutation.current?.abort(), []);\n useEffect(() => {\n const abort = new AbortController();\n setLoaded(null);\n setError(false);\n setConfirmation(null);\n setRollbackId(\"\");\n void Promise.resolve()\n .then(async () => {\n const snapshot = await loadSiteSnapshot(client, workspaceId, siteId, {\n signal: abort.signal,\n });\n if (!abort.signal.aborted) setLoaded(snapshot);\n })\n .catch(() => {\n if (!abort.signal.aborted) {\n setLoaded(null);\n setError(true);\n }\n });\n return () => abort.abort();\n }, [client, workspaceId, siteId, reload]);\n // Recheck live read authority without rebuilding the iframe on every tick.\n // No overlapping polls, and failures remove the document and tool bridge.\n useEffect(() => {\n if (!loaded || busy) return;\n const abort = new AbortController();\n let checking = false;\n const timer = setInterval(() => {\n if (checking) return;\n checking = true;\n void Promise.resolve()\n .then(() => client.getWorkspaceArtifact(workspaceId, siteId, { signal: abort.signal }))\n .then((detail) => {\n if (abort.signal.aborted) return;\n if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)\n throw new Error(\"Site scope mismatch\");\n if (\n detail.artifact.currentVersion?.id !== loaded.detail.artifact.currentVersion?.id ||\n detail.artifact.status !== loaded.detail.artifact.status\n ) {\n setLoaded(null);\n setReload((value) => value + 1);\n }\n })\n .catch(() => {\n if (!abort.signal.aborted) {\n setLoaded(null);\n setError(true);\n }\n })\n .finally(() => {\n checking = false;\n });\n }, 15_000);\n return () => {\n abort.abort();\n clearInterval(timer);\n };\n }, [client, workspaceId, siteId, loaded, busy]);\n const commit = async () => {\n const current = loaded?.detail.artifact.currentVersion;\n if (!canPublish || !loaded || !current || !confirmation || mutation.current) return;\n const abort = new AbortController();\n mutation.current = abort;\n setBusy(true);\n try {\n const common = { expectedCurrentVersionId: current.id, idempotencyKey: crypto.randomUUID() };\n if (confirmation === \"rollback\") {\n if (!loaded.detail.versions.some((version) => version.id === rollbackId))\n throw new Error(\"Unknown version\");\n await client.rollbackWorkspaceArtifact(\n workspaceId,\n siteId,\n { ...common, versionId: rollbackId, reason: \"Restore explicitly selected Site version\" },\n { signal: abort.signal },\n );\n } else {\n await client.setWorkspaceArtifactStatus(\n workspaceId,\n siteId,\n {\n ...common,\n status: loaded.detail.artifact.status === \"active\" ? \"archived\" : \"active\",\n reason: \"Explicit Site status change\",\n },\n { signal: abort.signal },\n );\n }\n if (!abort.signal.aborted) {\n setLoaded(null);\n setReload((value) => value + 1);\n }\n } catch {\n if (!abort.signal.aborted) {\n setLoaded(null);\n setError(true);\n }\n } finally {\n if (!abort.signal.aborted) {\n mutation.current = null;\n setBusy(false);\n setConfirmation(null);\n }\n }\n };\n const site = loaded?.detail.artifact;\n return (\n <section\n className={className}\n aria-label=\"Site details\"\n aria-busy={busy || (!loaded && !error)}\n >\n {error && (\n <p role=\"alert\">\n Site state could not be confirmed or access has changed. Refresh before continuing.\n </p>\n )}\n {!loaded && !error && <p role=\"status\">Loading Site…</p>}\n {site && (\n <>\n <h2>{site.title}</h2>\n <p>{site.description}</p>\n <p>\n {site.status} · revision {site.currentVersion?.revision ?? \"unpublished\"}\n </p>\n {loaded.content && (\n <PublishedHtmlArtifactFrame\n html={loaded.content.html}\n title={site.title}\n {...(toolBridge ? { toolBridge } : {})}\n />\n )}\n <h3>Versions</h3>\n <ul>\n {loaded.detail.versions.map((version) => (\n <li key={version.id}>\n Revision {version.revision} · {version.createdAt}\n {version.id === site.currentVersion?.id ? \" · current\" : \"\"}\n </li>\n ))}\n </ul>\n {loaded.detail.versionsTruncated && (\n <p>Older versions are not included in this response.</p>\n )}\n {canPublish && site.currentVersion && (\n <fieldset disabled={busy}>\n <legend>Manage Site</legend>\n <button type=\"button\" onClick={() => setConfirmation(\"status\")}>\n {site.status === \"active\" ? \"Archive Site\" : \"Restore Site\"}\n </button>\n <label>\n Version to restore\n <select\n value={rollbackId}\n onChange={(event) => {\n setRollbackId(event.target.value);\n setConfirmation(null);\n }}\n >\n <option value=\"\">Choose a version</option>\n {loaded.detail.versions\n .filter((version) => version.id !== site.currentVersion?.id)\n .map((version) => (\n <option key={version.id} value={version.id}>\n Revision {version.revision}\n </option>\n ))}\n </select>\n </label>\n <button\n type=\"button\"\n disabled={!rollbackId}\n onClick={() => setConfirmation(\"rollback\")}\n >\n Review rollback\n </button>\n {confirmation && (\n <div role=\"group\" aria-label=\"Confirm Site change\">\n <p>\n {confirmation === \"rollback\"\n ? \"Make the selected version current?\"\n : site.status === \"active\"\n ? \"Archive this Site for workspace users?\"\n : \"Restore this Site for workspace users?\"}\n </p>\n <button type=\"button\" onClick={() => void commit()}>\n Confirm change\n </button>\n <button type=\"button\" onClick={() => setConfirmation(null)}>\n Keep current state\n </button>\n </div>\n )}\n </fieldset>\n )}\n </>\n )}\n <button type=\"button\" disabled={busy} onClick={() => setReload((value) => value + 1)}>\n Refresh Site\n </button>\n </section>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type WorkbenchPeerLoader } from "./lib/workbench-peers.js";
|
|
2
|
+
export { SandboxTerminal } from "./components/sandbox-terminal.js";
|
|
3
|
+
export type { SandboxTerminalProps, XtermTheme } from "./components/sandbox-terminal.js";
|
|
4
|
+
export { registerSandboxTerminal } from "./lib/workbench-peers.js";
|
|
5
|
+
/** Opt in once on a terminal route; WebGL remains a separate optional import. */
|
|
6
|
+
export declare function enableSandboxTerminal(options?: {
|
|
7
|
+
webgl?: WorkbenchPeerLoader | undefined;
|
|
8
|
+
}): void;
|
package/dist/terminal.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SandboxTerminal
|
|
3
|
+
} from "./chunk-WRHIZYZF.js";
|
|
4
|
+
import "./chunk-YV4L3ILF.js";
|
|
5
|
+
import "./chunk-5J4FOW6Q.js";
|
|
6
|
+
import {
|
|
7
|
+
registerSandboxTerminal
|
|
8
|
+
} from "./chunk-JT4JW7BC.js";
|
|
9
|
+
import "./chunk-22KP6LR5.js";
|
|
10
|
+
|
|
11
|
+
// src/terminal.ts
|
|
12
|
+
async function loadTerminal() {
|
|
13
|
+
const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = await Promise.all([
|
|
14
|
+
import("@xterm/xterm"),
|
|
15
|
+
import("@xterm/addon-fit"),
|
|
16
|
+
import("@xterm/addon-web-links")
|
|
17
|
+
]);
|
|
18
|
+
return { Terminal, FitAddon, WebLinksAddon };
|
|
19
|
+
}
|
|
20
|
+
function enableSandboxTerminal(options) {
|
|
21
|
+
const loadWebgl = options?.webgl;
|
|
22
|
+
registerSandboxTerminal(
|
|
23
|
+
loadWebgl ? async () => ({ ...await loadTerminal(), loadWebgl }) : loadTerminal
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
SandboxTerminal,
|
|
28
|
+
enableSandboxTerminal,
|
|
29
|
+
registerSandboxTerminal
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=terminal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/terminal.ts"],"sourcesContent":["import {\n registerSandboxTerminal,\n type SandboxTerminalPeers,\n type WorkbenchPeerLoader,\n} from \"./lib/workbench-peers\";\n\nexport { SandboxTerminal } from \"./components/sandbox-terminal\";\nexport type { SandboxTerminalProps, XtermTheme } from \"./components/sandbox-terminal\";\nexport { registerSandboxTerminal } from \"./lib/workbench-peers\";\n\nasync function loadTerminal(): Promise<SandboxTerminalPeers> {\n const [{ Terminal }, { FitAddon }, { WebLinksAddon }] = await Promise.all([\n import(\"@xterm/xterm\"),\n import(\"@xterm/addon-fit\"),\n import(\"@xterm/addon-web-links\"),\n ]);\n return { Terminal, FitAddon, WebLinksAddon };\n}\n\n/** Opt in once on a terminal route; WebGL remains a separate optional import. */\nexport function enableSandboxTerminal(options?: { webgl?: WorkbenchPeerLoader | undefined }): void {\n const loadWebgl = options?.webgl;\n registerSandboxTerminal(\n loadWebgl ? async () => ({ ...(await loadTerminal()), loadWebgl }) : loadTerminal,\n );\n}\n"],"mappings":";;;;;;;;;;;AAUA,eAAe,eAA8C;AAC3D,QAAM,CAAC,EAAE,SAAS,GAAG,EAAE,SAAS,GAAG,EAAE,cAAc,CAAC,IAAI,MAAM,QAAQ,IAAI;AAAA,IACxE,OAAO,cAAc;AAAA,IACrB,OAAO,kBAAkB;AAAA,IACzB,OAAO,wBAAwB;AAAA,EACjC,CAAC;AACD,SAAO,EAAE,UAAU,UAAU,cAAc;AAC7C;AAGO,SAAS,sBAAsB,SAA6D;AACjG,QAAM,YAAY,SAAS;AAC3B;AAAA,IACE,YAAY,aAAa,EAAE,GAAI,MAAM,aAAa,GAAI,UAAU,KAAK;AAAA,EACvE;AACF;","names":[]}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { AllowanceExhaustedRefusal } from "@opengeni/sdk";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
import { type AllowanceLabels } from "../usage/allowance-copy.js";
|
|
4
|
+
/**
|
|
5
|
+
* Replace the built-in "usage limit reached" row. Return `undefined` to keep
|
|
6
|
+
* the default for this refusal (for example, only customize member limits).
|
|
7
|
+
*/
|
|
8
|
+
export type RenderAllowanceExhausted = (refusal: AllowanceExhaustedRefusal, context: {
|
|
9
|
+
labels: AllowanceLabels;
|
|
10
|
+
defaultRow: ReactNode;
|
|
11
|
+
}) => ReactNode | undefined;
|
|
12
|
+
export declare function AllowancePresentationProvider({ render, labels, children, }: {
|
|
13
|
+
render: RenderAllowanceExhausted | undefined;
|
|
14
|
+
labels: Partial<AllowanceLabels> | undefined;
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
/**
|
|
18
|
+
* A usage ceiling stopped further work. Calm by design: nothing failed, the
|
|
19
|
+
* conversation is kept, and the row says who can raise the limit and when it
|
|
20
|
+
* resets. Never the server's prose or a member identifier.
|
|
21
|
+
*/
|
|
22
|
+
export declare function AllowanceExhaustedRow({ refusal }: {
|
|
23
|
+
refusal: AllowanceExhaustedRefusal;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
2
|
export type GenieLoadingRenderProps = {
|
|
3
3
|
startedAt: string;
|
|
4
|
+
phase: "preparing" | "waiting";
|
|
4
5
|
detailsOpen: boolean;
|
|
5
6
|
onShowDetails: () => void;
|
|
6
7
|
};
|
|
@@ -25,8 +26,9 @@ export type GenieLoadingOptions = {
|
|
|
25
26
|
};
|
|
26
27
|
export declare const GenieLoadingOptionsContext: import("react").Context<GenieLoadingOptions | undefined>;
|
|
27
28
|
/** Decorative copy never substitutes for a failure or claims measurable progress. */
|
|
28
|
-
export declare function GenieLoading({ startedAt, onShowDetails, detailsOpen, }: {
|
|
29
|
+
export declare function GenieLoading({ startedAt, phase, onShowDetails, detailsOpen, }: {
|
|
29
30
|
startedAt: string;
|
|
31
|
+
phase?: GenieLoadingRenderProps["phase"];
|
|
30
32
|
onShowDetails: () => void;
|
|
31
33
|
detailsOpen?: boolean;
|
|
32
34
|
}): string | number | bigint | boolean | import("react").JSX.Element | Iterable<ReactNode> | Promise<string | number | bigint | boolean | Iterable<ReactNode> | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | import("react").ReactPortal | null | undefined> | null | undefined;
|
package/dist/timeline/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HumanInputQuestion, HumanInputResponse, MediaGenerationResult, ResourceRef, SessionStatus, TimelineAnnotation, TimelineAnnotationSource, ToolAuthNeededPayload, ToolRef, ToolDisplayMetadata } from "@opengeni/sdk";
|
|
1
|
+
import type { AllowanceExhaustedRefusal, HumanInputQuestion, HumanInputResponse, MediaGenerationResult, ResourceRef, SessionStatus, TimelineAnnotation, TimelineAnnotationSource, ToolAuthNeededPayload, ToolRef, ToolDisplayMetadata } from "@opengeni/sdk";
|
|
2
2
|
export type TimelineAnnotationSourceDescriptor = Omit<TimelineAnnotationSource, "startOffset" | "endOffset" | "contextBefore" | "contextAfter"> & {
|
|
3
3
|
text: string;
|
|
4
4
|
};
|
|
@@ -30,6 +30,7 @@ export type UserMessageItem = {
|
|
|
30
30
|
state: "sending" | "queued" | "failed";
|
|
31
31
|
error?: string | undefined;
|
|
32
32
|
onRetry?: (() => void) | undefined;
|
|
33
|
+
onEdit?: (() => void) | undefined;
|
|
33
34
|
onRemove?: (() => void) | undefined;
|
|
34
35
|
} | undefined;
|
|
35
36
|
};
|
|
@@ -309,6 +310,12 @@ export type NoticeItem = {
|
|
|
309
310
|
label: string;
|
|
310
311
|
url: string;
|
|
311
312
|
};
|
|
313
|
+
/**
|
|
314
|
+
* A usage ceiling refused further work. `text` keeps the canonical sentence
|
|
315
|
+
* for plain-text consumers; `MessageTimeline` renders this structured row
|
|
316
|
+
* (customizable with `renderAllowanceExhausted` / `allowanceExhaustedLabels`).
|
|
317
|
+
*/
|
|
318
|
+
allowance?: AllowanceExhaustedRefusal;
|
|
312
319
|
occurredAt: string;
|
|
313
320
|
};
|
|
314
321
|
/**
|
package/dist/{timeline-annotation-selection-ODLVF4OI.js → timeline-annotation-selection-DMLMM2TX.js}
RENAMED
|
@@ -133,6 +133,7 @@ function TimelineAnnotationSelection({
|
|
|
133
133
|
}) {
|
|
134
134
|
const [candidate, setCandidate] = useState(null);
|
|
135
135
|
const buttonRef = useRef(null);
|
|
136
|
+
const initialSelectionObservedRef = useRef(false);
|
|
136
137
|
const pointerOriginRef = useRef("unset");
|
|
137
138
|
const portalStyle = usePortalTokenStyle(rootRef.current);
|
|
138
139
|
useEffect(() => {
|
|
@@ -144,6 +145,10 @@ function TimelineAnnotationSelection({
|
|
|
144
145
|
const update = (keyboard) => {
|
|
145
146
|
setCandidate(buildCandidate(root, sources, keyboard));
|
|
146
147
|
};
|
|
148
|
+
if (!initialSelectionObservedRef.current) {
|
|
149
|
+
initialSelectionObservedRef.current = true;
|
|
150
|
+
update(false);
|
|
151
|
+
}
|
|
147
152
|
const onRootPointerUp = (event) => {
|
|
148
153
|
if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;
|
|
149
154
|
if (pointerOriginRef.current === "outside") return;
|
|
@@ -233,4 +238,4 @@ export {
|
|
|
233
238
|
TimelineAnnotationSelection,
|
|
234
239
|
timeline_annotation_selection_default as default
|
|
235
240
|
};
|
|
236
|
-
//# sourceMappingURL=timeline-annotation-selection-
|
|
241
|
+
//# sourceMappingURL=timeline-annotation-selection-DMLMM2TX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/timeline-annotation-selection.tsx"],"sourcesContent":["import type { DraftTimelineAnnotation } from \"@opengeni/sdk\";\nimport { QuoteIcon } from \"lucide-react\";\nimport { useEffect, useRef, useState, type RefObject } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport type { TimelineAnnotationSourceDescriptor } from \"../timeline\";\nimport {\n clipRangeToSource,\n matchingQuoteInSource,\n occurrenceOffsets,\n quoteOccurrenceIndex,\n truncateAnnotationQuote,\n visibleClientRect,\n} from \"./timeline-annotation-shared\";\n\ntype SelectionCandidate = {\n annotation: DraftTimelineAnnotation;\n left: number;\n top: number;\n keyboard: boolean;\n};\n\nconst CONTROL_SELECTOR = \"button,input,textarea,select,summary,[contenteditable='true']\";\nconst SOURCE_CONTEXT_BYTES = 160;\nconst MAX_QUOTE_BYTES = 16 * 1024;\nconst POPOVER_WIDTH = 280;\nconst POPOVER_HEIGHT = 64;\n\nfunction utf8Prefix(value: string, maxBytes: number): string {\n let output = \"\";\n let bytes = 0;\n for (const character of value) {\n const next = new TextEncoder().encode(character).byteLength;\n if (bytes + next > maxBytes) break;\n output += character;\n bytes += next;\n }\n return output;\n}\n\nfunction utf8Suffix(value: string, maxBytes: number): string {\n let output = \"\";\n let bytes = 0;\n for (const character of [...value].reverse()) {\n const next = new TextEncoder().encode(character).byteLength;\n if (bytes + next > maxBytes) break;\n output = character + output;\n bytes += next;\n }\n return output;\n}\n\nfunction selectionElement(node: Node | null): Element | null {\n return node instanceof Element ? node : (node?.parentElement ?? null);\n}\n\nfunction containingSource(node: Node | null): HTMLElement | null {\n const element = selectionElement(node);\n const source = element?.closest(\"[data-og-annotation-source-key]\");\n return source instanceof HTMLElement ? source : null;\n}\n\nfunction resolveSourceBoundary(range: Range, root: HTMLElement): HTMLElement | null {\n const start = containingSource(range.startContainer);\n const end = containingSource(range.endContainer);\n if (start && end && start !== end) return null;\n const boundary = start ?? end;\n if (!boundary || !root.contains(boundary)) return null;\n return boundary;\n}\n\nfunction isWhollyInsideControl(range: Range, boundary: HTMLElement): boolean {\n const start = selectionElement(range.startContainer)?.closest(CONTROL_SELECTOR);\n const end = selectionElement(range.endContainer)?.closest(CONTROL_SELECTOR);\n return (\n start instanceof Element && start === end && boundary.contains(start) && start !== boundary\n );\n}\n\nfunction annotationId(): string {\n if (typeof globalThis.crypto?.randomUUID === \"function\") {\n return globalThis.crypto.randomUUID();\n }\n return `annotation-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n\nfunction popoverPosition(rect: DOMRect): { left: number; top: number } {\n const half = POPOVER_WIDTH / 2;\n const left = clamp(rect.left + rect.width / 2, 12 + half, window.innerWidth - 12 - half);\n const preferBelow = rect.bottom + 8 + POPOVER_HEIGHT <= window.innerHeight - 12;\n const top = preferBelow ? rect.bottom + 8 : Math.max(12, rect.top - POPOVER_HEIGHT - 8);\n return { left, top };\n}\n\nfunction buildCandidate(\n root: HTMLElement,\n sources: ReadonlyMap<string, TimelineAnnotationSourceDescriptor>,\n keyboard: boolean,\n): SelectionCandidate | null {\n const selection = document.getSelection();\n if (!selection || selection.rangeCount !== 1 || selection.isCollapsed) return null;\n const range = selection.getRangeAt(0);\n const startBoundary = resolveSourceBoundary(range, root);\n if (!startBoundary) return null;\n if (isWhollyInsideControl(range, startBoundary)) return null;\n const clipped = clipRangeToSource(range, startBoundary);\n if (!clipped) return null;\n const sourceKey = startBoundary.dataset.ogAnnotationSourceKey;\n const source = sourceKey ? sources.get(sourceKey) : undefined;\n const rawQuote = clipped.toString();\n const quote = (source ? matchingQuoteInSource(source.text, rawQuote) : null) ?? rawQuote;\n if (\n !source ||\n quote.trim().length === 0 ||\n new TextEncoder().encode(quote).byteLength > MAX_QUOTE_BYTES\n ) {\n return null;\n }\n const sourceOffsets = occurrenceOffsets(source.text, quote);\n if (sourceOffsets.length === 0) return null;\n const occurrence = quoteOccurrenceIndex(startBoundary, clipped, quote);\n const startOffset = sourceOffsets[Math.min(occurrence, sourceOffsets.length - 1)]!;\n const endOffset = startOffset + quote.length;\n const rect = visibleClientRect(range) ?? visibleClientRect(clipped);\n if (!rect) return null;\n const position = popoverPosition(rect);\n return {\n annotation: {\n id: annotationId(),\n quote,\n note: \"\",\n source: {\n kind: source.kind,\n eventId: source.eventId,\n eventType: source.eventType,\n sequence: source.sequence,\n turnId: source.turnId,\n startOffset,\n endOffset,\n contextBefore: utf8Suffix(source.text.slice(0, startOffset), SOURCE_CONTEXT_BYTES),\n contextAfter: utf8Prefix(source.text.slice(endOffset), SOURCE_CONTEXT_BYTES),\n ...(source.label ? { label: source.label } : {}),\n },\n },\n left: position.left,\n top: position.top,\n keyboard,\n };\n}\n\nexport function TimelineAnnotationSelection({\n rootRef,\n sources,\n onAnnotate,\n}: {\n rootRef: RefObject<HTMLElement | null>;\n sources: ReadonlyMap<string, TimelineAnnotationSourceDescriptor>;\n onAnnotate?: ((annotation: DraftTimelineAnnotation) => void) | undefined;\n}) {\n const [candidate, setCandidate] = useState<SelectionCandidate | null>(null);\n const buttonRef = useRef<HTMLButtonElement | null>(null);\n const initialSelectionObservedRef = useRef(false);\n const pointerOriginRef = useRef<\"unset\" | \"inside\" | \"outside\">(\"unset\");\n const portalStyle = usePortalTokenStyle(rootRef.current);\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root || !onAnnotate) {\n setCandidate(null);\n return;\n }\n const update = (keyboard: boolean) => {\n setCandidate(buildCandidate(root, sources, keyboard));\n };\n // A user may finish selecting text before this lazy component arrives.\n // Reconcile once; later prop changes must not revive dismissed selections.\n if (!initialSelectionObservedRef.current) {\n initialSelectionObservedRef.current = true;\n update(false);\n }\n const onRootPointerUp = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;\n // Tests may fire only pointerup on the source. A real click that started\n // outside the timeline must not resurrect leftover selection.\n if (pointerOriginRef.current === \"outside\") return;\n window.setTimeout(() => update(false), 0);\n };\n const onDocumentPointerUp = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) return;\n const startedInRoot = pointerOriginRef.current === \"inside\";\n pointerOriginRef.current = \"unset\";\n if (!startedInRoot) return;\n window.setTimeout(() => update(false), 0);\n };\n const onKeyUp = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") {\n setCandidate(null);\n return;\n }\n window.setTimeout(() => update(true), 0);\n };\n const onPointerDown = (event: PointerEvent) => {\n if (event.target instanceof Node && buttonRef.current?.contains(event.target)) {\n event.preventDefault();\n return;\n }\n pointerOriginRef.current =\n event.target instanceof Node && root.contains(event.target) ? \"inside\" : \"outside\";\n setCandidate(null);\n };\n const onDocumentKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") setCandidate(null);\n };\n const onSelectionChange = () => {\n if (buttonRef.current && document.activeElement === buttonRef.current) return;\n const selection = document.getSelection();\n if (!selection || selection.isCollapsed) setCandidate(null);\n };\n root.addEventListener(\"pointerup\", onRootPointerUp);\n root.addEventListener(\"keyup\", onKeyUp);\n document.addEventListener(\"pointerup\", onDocumentPointerUp);\n document.addEventListener(\"pointerdown\", onPointerDown);\n document.addEventListener(\"keydown\", onDocumentKeyDown);\n document.addEventListener(\"selectionchange\", onSelectionChange);\n return () => {\n root.removeEventListener(\"pointerup\", onRootPointerUp);\n root.removeEventListener(\"keyup\", onKeyUp);\n document.removeEventListener(\"pointerup\", onDocumentPointerUp);\n document.removeEventListener(\"pointerdown\", onPointerDown);\n document.removeEventListener(\"keydown\", onDocumentKeyDown);\n document.removeEventListener(\"selectionchange\", onSelectionChange);\n };\n }, [onAnnotate, rootRef, sources]);\n\n useEffect(() => {\n if (candidate?.keyboard) buttonRef.current?.focus();\n }, [candidate]);\n\n if (!candidate || !onAnnotate || typeof document === \"undefined\") return null;\n const preview = truncateAnnotationQuote(candidate.annotation.quote, 72);\n return createPortal(\n <button\n ref={buttonRef}\n type=\"button\"\n data-og-annotation-popover=\"\"\n style={{ left: candidate.left, top: candidate.top, ...portalStyle }}\n className=\"og-root fixed z-[80] flex max-w-[min(18rem,calc(100vw-1.5rem))] -translate-x-1/2 items-start gap-2 rounded-2xl border border-og-border bg-og-surface-1 px-3 py-2 text-og-sm font-medium text-og-fg shadow-xl outline-hidden transition hover:border-og-accent/40 hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent pointer-coarse:min-h-[44px]\"\n aria-label={`Add a note about “${preview}”`}\n title={candidate.annotation.quote}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => {\n onAnnotate(candidate.annotation);\n document.getSelection()?.removeAllRanges();\n setCandidate(null);\n }}\n >\n <QuoteIcon className=\"mt-0.5 size-3.5 shrink-0 text-og-accent\" aria-hidden=\"true\" />\n <span className=\"min-w-0 text-left\">\n <span className=\"block leading-5\">Add note</span>\n <span className=\"mt-0.5 block truncate text-og-xs font-normal text-og-fg-muted\">\n {preview}\n </span>\n </span>\n </button>,\n document.body,\n );\n}\n\nexport default TimelineAnnotationSelection;\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// src/components/timeline-pill-geometry.ts
|
|
2
|
+
var INTERACTIVE = 'a[href], button, input, select, textarea, summary, [role="button"], [role="link"], [role="menuitem"], [role="tab"], [role="switch"], [role="checkbox"], [contenteditable="true"]';
|
|
3
|
+
var ROW_WIDTH_RATIO = 0.8;
|
|
4
|
+
function chooseQuestionPosition(pill, frame, scroller, preference, current) {
|
|
5
|
+
const rect = pill.getBoundingClientRect();
|
|
6
|
+
const bounds = frame.getBoundingClientRect();
|
|
7
|
+
const view = scroller.getBoundingClientRect();
|
|
8
|
+
if (!rect.width || !view.width) return current;
|
|
9
|
+
const style = getComputedStyle(frame);
|
|
10
|
+
const baseTop = bounds.top - current.offsetY;
|
|
11
|
+
const left = (placement) => placement === "start" ? bounds.left + parseFloat(style.paddingLeft || "0") : placement === "end" ? bounds.right - parseFloat(style.paddingRight || "0") - rect.width : bounds.left + (bounds.width - rect.width) / 2;
|
|
12
|
+
const controls = [];
|
|
13
|
+
for (const control of scroller.querySelectorAll(INTERACTIVE)) {
|
|
14
|
+
const box = control.getBoundingClientRect();
|
|
15
|
+
if (!box.width || !box.height || box.bottom <= baseTop || box.top >= view.bottom) continue;
|
|
16
|
+
if (box.width < view.width * ROW_WIDTH_RATIO) controls.push(box);
|
|
17
|
+
else {
|
|
18
|
+
for (const icon of control.querySelectorAll("svg, img, canvas, video"))
|
|
19
|
+
controls.push(icon.getBoundingClientRect());
|
|
20
|
+
const walker = scroller.ownerDocument.createTreeWalker(control, NodeFilter.SHOW_TEXT);
|
|
21
|
+
for (let text = walker.nextNode(); text; text = walker.nextNode()) {
|
|
22
|
+
if (!text.textContent?.trim()) continue;
|
|
23
|
+
const range = scroller.ownerDocument.createRange();
|
|
24
|
+
range.selectNodeContents(text);
|
|
25
|
+
controls.push(...range.getClientRects());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const clear = (placement, offsetY) => !controls.some(
|
|
30
|
+
(control) => control.right > left(placement) && control.left < left(placement) + rect.width && control.bottom > baseTop + offsetY && control.top < baseTop + offsetY + rect.height
|
|
31
|
+
);
|
|
32
|
+
const order = [current.placement, ...preference.filter((value) => value !== current.placement)];
|
|
33
|
+
for (const placement of order) if (clear(placement, 0)) return { placement, offsetY: 0 };
|
|
34
|
+
const offsets = [...new Set(controls.map((control) => control.bottom + 4 - baseTop))].filter((offset) => offset > 0 && baseTop + offset + rect.height <= view.bottom).sort((a, b) => a - b);
|
|
35
|
+
for (const offsetY of offsets) {
|
|
36
|
+
for (const placement of order) if (clear(placement, offsetY)) return { placement, offsetY };
|
|
37
|
+
}
|
|
38
|
+
return current;
|
|
39
|
+
}
|
|
40
|
+
function choosePlacement(pill, frame, scroller, preference, current) {
|
|
41
|
+
const pillRect = pill.getBoundingClientRect();
|
|
42
|
+
const frameRect = frame.getBoundingClientRect();
|
|
43
|
+
const view = scroller.getBoundingClientRect();
|
|
44
|
+
if (pillRect.width === 0 || view.width === 0) return current;
|
|
45
|
+
const inset = Math.max(0, pillRect.left - frameRect.left, frameRect.right - pillRect.right);
|
|
46
|
+
const gutter = Math.min(inset, parseFloat(getComputedStyle(scroller).paddingLeft) || 16);
|
|
47
|
+
const left = (placement) => placement === "start" ? frameRect.left + gutter : placement === "end" ? frameRect.right - gutter - pillRect.width : frameRect.left + (frameRect.width - pillRect.width) / 2;
|
|
48
|
+
const collisions = (placement) => countCollisions(
|
|
49
|
+
pill,
|
|
50
|
+
scroller,
|
|
51
|
+
view,
|
|
52
|
+
left(placement),
|
|
53
|
+
pillRect.top,
|
|
54
|
+
pillRect.width,
|
|
55
|
+
pillRect.height
|
|
56
|
+
);
|
|
57
|
+
if (collisions(current) === 0) return current;
|
|
58
|
+
let best = current;
|
|
59
|
+
let fewest = Number.POSITIVE_INFINITY;
|
|
60
|
+
for (const candidate of preference) {
|
|
61
|
+
const count = candidate === current ? collisions(current) : collisions(candidate);
|
|
62
|
+
if (count === 0) return candidate;
|
|
63
|
+
if (count < fewest) {
|
|
64
|
+
fewest = count;
|
|
65
|
+
best = candidate;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return best;
|
|
69
|
+
}
|
|
70
|
+
function countCollisions(pill, scroller, view, left, top, width, height) {
|
|
71
|
+
const document = scroller.ownerDocument;
|
|
72
|
+
let count = 0;
|
|
73
|
+
for (const fx of [0.06, 0.3, 0.5, 0.7, 0.94]) {
|
|
74
|
+
for (const fy of [0.2, 0.5, 0.8]) {
|
|
75
|
+
const x = left + width * fx;
|
|
76
|
+
const y = top + height * fy;
|
|
77
|
+
if (x < view.left || x > view.right || y < view.top || y > view.bottom) continue;
|
|
78
|
+
if (coversControl(document.elementsFromPoint(x, y), pill, scroller, view, x, y)) count += 1;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return count;
|
|
82
|
+
}
|
|
83
|
+
function coversControl(stack, pill, scroller, view, x, y) {
|
|
84
|
+
for (const element of stack) {
|
|
85
|
+
if (pill.contains(element)) continue;
|
|
86
|
+
if (!scroller.contains(element) || element === scroller) return false;
|
|
87
|
+
const control = element.closest(INTERACTIVE);
|
|
88
|
+
if (!control || !scroller.contains(control)) return false;
|
|
89
|
+
if (control.getBoundingClientRect().width < view.width * ROW_WIDTH_RATIO) return true;
|
|
90
|
+
if (element.closest("svg, img, canvas, video")) return true;
|
|
91
|
+
return textUnderPoint(element, x, y);
|
|
92
|
+
}
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
function textUnderPoint(element, x, y) {
|
|
96
|
+
const range = element.ownerDocument.createRange();
|
|
97
|
+
for (const node of element.childNodes) {
|
|
98
|
+
if (node.nodeType !== Node.TEXT_NODE || !node.textContent?.trim()) continue;
|
|
99
|
+
range.selectNodeContents(node);
|
|
100
|
+
for (const rect of range.getClientRects()) {
|
|
101
|
+
if (x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom) return true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
choosePlacement,
|
|
108
|
+
chooseQuestionPosition
|
|
109
|
+
};
|
|
110
|
+
//# sourceMappingURL=timeline-pill-geometry-Z23V243M.js.map
|