@opengeni/react 4.0.2 → 5.0.4-canary.0
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 +42 -0
- package/dist/artifacts.js +24 -285
- package/dist/artifacts.js.map +1 -1
- package/dist/chunk-BRFMGY4L.js +274 -0
- package/dist/chunk-BRFMGY4L.js.map +1 -0
- package/dist/chunk-IWIDV7SB.js +1 -0
- package/dist/chunk-IWIDV7SB.js.map +1 -0
- package/dist/{chunk-CQG7YUJB.js → chunk-SCZ6SUBW.js} +1177 -1021
- package/dist/chunk-SCZ6SUBW.js.map +1 -0
- package/dist/clipboard.js +1 -0
- package/dist/components/markdown-table-layout.d.ts +4 -1
- package/dist/components/message-timeline.d.ts +7 -3
- package/dist/connect-accounts.d.ts +10 -0
- package/dist/connect-chooser.d.ts +11 -0
- package/dist/connect-panel.d.ts +10 -0
- package/dist/connect-setup.d.ts +12 -0
- package/dist/connect.d.ts +30 -0
- package/dist/connect.js +831 -0
- package/dist/connect.js.map +1 -0
- package/dist/device-authorization.d.ts +15 -0
- package/dist/identity-link-accounts.d.ts +11 -0
- package/dist/identity-link-consent.d.ts +18 -0
- package/dist/index.js +523 -663
- package/dist/index.js.map +1 -1
- package/dist/{markdown-table-layout-3Z3AWBY7.js → markdown-table-layout-PFUEZ2U4.js} +7 -4
- package/dist/{markdown-table-layout-3Z3AWBY7.js.map → markdown-table-layout-PFUEZ2U4.js.map} +1 -1
- package/dist/session-ui.d.ts +2 -0
- package/dist/session-ui.js +3 -1
- package/dist/sites-ui.d.ts +33 -0
- package/dist/sites.d.ts +3 -0
- package/dist/sites.js +288 -0
- package/dist/sites.js.map +1 -0
- package/package.json +16 -2
- package/src/components/markdown-table-layout.ts +6 -3
- package/src/components/markdown.tsx +11 -3
- package/src/components/message-timeline.tsx +22 -5
- package/src/components/sandbox-workspace.tsx +9 -2
- package/src/connect-accounts.tsx +171 -0
- package/src/connect-chooser.tsx +144 -0
- package/src/connect-panel.tsx +29 -0
- package/src/connect-setup.tsx +260 -0
- package/src/connect.ts +48 -0
- package/src/device-authorization.tsx +90 -0
- package/src/hooks/use-workspace-capture.ts +3 -2
- package/src/identity-link-accounts.tsx +144 -0
- package/src/identity-link-consent.tsx +192 -0
- package/src/session-ui.ts +2 -0
- package/src/sites-ui.tsx +385 -0
- package/src/sites.ts +3 -0
- package/src/timeline/tool-renderers.tsx +1 -1
- package/styles/connect.css +48 -0
- package/styles/connect.d.ts +1 -0
- package/dist/chunk-CQG7YUJB.js.map +0 -1
|
@@ -59,13 +59,16 @@ function observeMarkdownTableLayout(wrapper, table) {
|
|
|
59
59
|
observer?.observe(scroller);
|
|
60
60
|
observer?.observe(body);
|
|
61
61
|
observer?.observe(table);
|
|
62
|
-
return
|
|
63
|
-
|
|
64
|
-
|
|
62
|
+
return {
|
|
63
|
+
measure,
|
|
64
|
+
disconnect: () => {
|
|
65
|
+
observer?.disconnect();
|
|
66
|
+
reset();
|
|
67
|
+
}
|
|
65
68
|
};
|
|
66
69
|
}
|
|
67
70
|
export {
|
|
68
71
|
markdownTableWidth,
|
|
69
72
|
observeMarkdownTableLayout
|
|
70
73
|
};
|
|
71
|
-
//# sourceMappingURL=markdown-table-layout-
|
|
74
|
+
//# sourceMappingURL=markdown-table-layout-PFUEZ2U4.js.map
|
package/dist/{markdown-table-layout-3Z3AWBY7.js.map → markdown-table-layout-PFUEZ2U4.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/markdown-table-layout.ts"],"sourcesContent":["export function markdownTableWidth({\n columnLeft,\n columnWidth,\n contentLeft,\n contentRight,\n preferredWidth,\n}: {\n columnLeft: number;\n columnWidth: number;\n contentLeft: number;\n contentRight: number;\n preferredWidth: number;\n}): number {\n const center = columnLeft + columnWidth / 2;\n const available = 2 * Math.min(center - contentLeft, contentRight - center);\n return Math.max(columnWidth, Math.min(preferredWidth, available));\n}\n\n/** Loaded only for assistant tables. Keep prose and nested scroll owners intact. */\nexport function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLTableElement) {\n const body = wrapper.parentElement;\n const scroller = body\n ?.closest(\"[data-og-wide-table-message]\")\n ?.closest<HTMLElement>(\"[data-og-timeline-scroller]\");\n if (!body?.classList.contains(\"og-markdown-body\") || !scroller) return;\n\n let width: number | null = null;\n const reset = () => {\n width = null;\n wrapper.style.width = \"\";\n wrapper.style.maxWidth = \"\";\n wrapper.style.marginInline = \"\";\n };\n const measure = () => {\n // Intermediate clipped/scrollable surfaces own their own content bounds.\n for (\n let ancestor: HTMLElement | null = body;\n ancestor && ancestor !== scroller;\n ancestor = ancestor.parentElement\n ) {\n if (getComputedStyle(ancestor).overflowX !== \"visible\") {\n reset();\n return;\n }\n }\n const column = body.getBoundingClientRect();\n const panel = scroller.getBoundingClientRect();\n const panelStyle = getComputedStyle(scroller);\n const left = panel.left + scroller.clientLeft + parseFloat(panelStyle.paddingLeft);\n const right =\n panel.left + scroller.clientLeft + scroller.clientWidth - parseFloat(panelStyle.paddingRight);\n\n // Measure the original table, preserving its wrapping and TSV copy DOM.\n const previousWidth = table.style.width;\n let preferred: number;\n try {\n table.style.width = \"max-content\";\n preferred = table.getBoundingClientRect().width;\n } finally {\n table.style.width = previousWidth;\n }\n const next = markdownTableWidth({\n columnLeft: column.left,\n columnWidth: column.width,\n contentLeft: left,\n contentRight: right,\n preferredWidth: preferred,\n });\n if (width !== null && Math.abs(width - next) < 0.5) return;\n width = next;\n wrapper.style.width = `${width}px`;\n wrapper.style.maxWidth = \"none\";\n wrapper.style.marginInline = `calc((100% - ${width}px) / 2)`;\n };\n\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(scroller);\n observer?.observe(body);\n observer?.observe(table);\n return () => {\n
|
|
1
|
+
{"version":3,"sources":["../src/components/markdown-table-layout.ts"],"sourcesContent":["export function markdownTableWidth({\n columnLeft,\n columnWidth,\n contentLeft,\n contentRight,\n preferredWidth,\n}: {\n columnLeft: number;\n columnWidth: number;\n contentLeft: number;\n contentRight: number;\n preferredWidth: number;\n}): number {\n const center = columnLeft + columnWidth / 2;\n const available = 2 * Math.min(center - contentLeft, contentRight - center);\n return Math.max(columnWidth, Math.min(preferredWidth, available));\n}\n\n/** Loaded only for assistant tables. Keep prose and nested scroll owners intact. */\nexport function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLTableElement) {\n const body = wrapper.parentElement;\n const scroller = body\n ?.closest(\"[data-og-wide-table-message]\")\n ?.closest<HTMLElement>(\"[data-og-timeline-scroller]\");\n if (!body?.classList.contains(\"og-markdown-body\") || !scroller) return;\n\n let width: number | null = null;\n const reset = () => {\n width = null;\n wrapper.style.width = \"\";\n wrapper.style.maxWidth = \"\";\n wrapper.style.marginInline = \"\";\n };\n const measure = () => {\n // Intermediate clipped/scrollable surfaces own their own content bounds.\n for (\n let ancestor: HTMLElement | null = body;\n ancestor && ancestor !== scroller;\n ancestor = ancestor.parentElement\n ) {\n if (getComputedStyle(ancestor).overflowX !== \"visible\") {\n reset();\n return;\n }\n }\n const column = body.getBoundingClientRect();\n const panel = scroller.getBoundingClientRect();\n const panelStyle = getComputedStyle(scroller);\n const left = panel.left + scroller.clientLeft + parseFloat(panelStyle.paddingLeft);\n const right =\n panel.left + scroller.clientLeft + scroller.clientWidth - parseFloat(panelStyle.paddingRight);\n\n // Measure the original table, preserving its wrapping and TSV copy DOM.\n const previousWidth = table.style.width;\n let preferred: number;\n try {\n table.style.width = \"max-content\";\n preferred = table.getBoundingClientRect().width;\n } finally {\n table.style.width = previousWidth;\n }\n const next = markdownTableWidth({\n columnLeft: column.left,\n columnWidth: column.width,\n contentLeft: left,\n contentRight: right,\n preferredWidth: preferred,\n });\n if (width !== null && Math.abs(width - next) < 0.5) return;\n width = next;\n wrapper.style.width = `${width}px`;\n wrapper.style.maxWidth = \"none\";\n wrapper.style.marginInline = `calc((100% - ${width}px) / 2)`;\n };\n\n measure();\n const observer = typeof ResizeObserver === \"undefined\" ? undefined : new ResizeObserver(measure);\n observer?.observe(scroller);\n observer?.observe(body);\n observer?.observe(table);\n return {\n measure,\n disconnect: () => {\n observer?.disconnect();\n reset();\n },\n };\n}\n"],"mappings":";AAAO,SAAS,mBAAmB;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAMW;AACT,QAAM,SAAS,aAAa,cAAc;AAC1C,QAAM,YAAY,IAAI,KAAK,IAAI,SAAS,aAAa,eAAe,MAAM;AAC1E,SAAO,KAAK,IAAI,aAAa,KAAK,IAAI,gBAAgB,SAAS,CAAC;AAClE;AAGO,SAAS,2BAA2B,SAAyB,OAAyB;AAC3F,QAAM,OAAO,QAAQ;AACrB,QAAM,WAAW,MACb,QAAQ,8BAA8B,GACtC,QAAqB,6BAA6B;AACtD,MAAI,CAAC,MAAM,UAAU,SAAS,kBAAkB,KAAK,CAAC,SAAU;AAEhE,MAAI,QAAuB;AAC3B,QAAM,QAAQ,MAAM;AAClB,YAAQ;AACR,YAAQ,MAAM,QAAQ;AACtB,YAAQ,MAAM,WAAW;AACzB,YAAQ,MAAM,eAAe;AAAA,EAC/B;AACA,QAAM,UAAU,MAAM;AAEpB,aACM,WAA+B,MACnC,YAAY,aAAa,UACzB,WAAW,SAAS,eACpB;AACA,UAAI,iBAAiB,QAAQ,EAAE,cAAc,WAAW;AACtD,cAAM;AACN;AAAA,MACF;AAAA,IACF;AACA,UAAM,SAAS,KAAK,sBAAsB;AAC1C,UAAM,QAAQ,SAAS,sBAAsB;AAC7C,UAAM,aAAa,iBAAiB,QAAQ;AAC5C,UAAM,OAAO,MAAM,OAAO,SAAS,aAAa,WAAW,WAAW,WAAW;AACjF,UAAM,QACJ,MAAM,OAAO,SAAS,aAAa,SAAS,cAAc,WAAW,WAAW,YAAY;AAG9F,UAAM,gBAAgB,MAAM,MAAM;AAClC,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,QAAQ;AACpB,kBAAY,MAAM,sBAAsB,EAAE;AAAA,IAC5C,UAAE;AACA,YAAM,MAAM,QAAQ;AAAA,IACtB;AACA,UAAM,OAAO,mBAAmB;AAAA,MAC9B,YAAY,OAAO;AAAA,MACnB,aAAa,OAAO;AAAA,MACpB,aAAa;AAAA,MACb,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB,CAAC;AACD,QAAI,UAAU,QAAQ,KAAK,IAAI,QAAQ,IAAI,IAAI,IAAK;AACpD,YAAQ;AACR,YAAQ,MAAM,QAAQ,GAAG,KAAK;AAC9B,YAAQ,MAAM,WAAW;AACzB,YAAQ,MAAM,eAAe,gBAAgB,KAAK;AAAA,EACpD;AAEA,UAAQ;AACR,QAAM,WAAW,OAAO,mBAAmB,cAAc,SAAY,IAAI,eAAe,OAAO;AAC/F,YAAU,QAAQ,QAAQ;AAC1B,YAAU,QAAQ,IAAI;AACtB,YAAU,QAAQ,KAAK;AACvB,SAAO;AAAA,IACL;AAAA,IACA,YAAY,MAAM;AAChB,gBAAU,WAAW;AACrB,YAAM;AAAA,IACR;AAAA,EACF;AACF;","names":[]}
|
package/dist/session-ui.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
export { HumanInputForm } from "./components/human-input-form.js";
|
|
2
2
|
export type { HumanInputAnswerDraft, HumanInputFormMessages, HumanInputFormProps, } from "./components/human-input-form.js";
|
|
3
3
|
export { HumanInputSurface } from "./components/human-input-surface.js";
|
|
4
|
+
export { ApprovalSurface } from "./components/approval-surface.js";
|
|
5
|
+
export type { ApprovalSurfaceProps, ApprovalSurfaceMessages } from "./components/approval-surface.js";
|
|
4
6
|
export type { HumanInputSurfaceProps } from "./components/human-input-surface.js";
|
|
5
7
|
export { MessageTimeline, TimelineRow } from "./components/message-timeline.js";
|
|
6
8
|
export type { MessageTimelineProps } from "./components/message-timeline.js";
|
package/dist/session-ui.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ApprovalSurface,
|
|
2
3
|
BUILT_IN_TURN_SUMMARY_FACET_IDS,
|
|
3
4
|
HumanInputForm,
|
|
4
5
|
HumanInputSurface,
|
|
@@ -13,7 +14,7 @@ import {
|
|
|
13
14
|
sessionChromeGoalPillLabel,
|
|
14
15
|
sessionChromeGoalPillState,
|
|
15
16
|
userMessageLikelyNeedsDisclosure
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-SCZ6SUBW.js";
|
|
17
18
|
import "./chunk-YBM6CQRU.js";
|
|
18
19
|
import "./chunk-5M6VTFJ5.js";
|
|
19
20
|
import "./chunk-ZYRGEKWS.js";
|
|
@@ -33,6 +34,7 @@ import "./chunk-NEXLLTP4.js";
|
|
|
33
34
|
import "./chunk-VRBL3F5E.js";
|
|
34
35
|
import "./chunk-22KP6LR5.js";
|
|
35
36
|
export {
|
|
37
|
+
ApprovalSurface,
|
|
36
38
|
BUILT_IN_TURN_SUMMARY_FACET_IDS,
|
|
37
39
|
HumanInputForm,
|
|
38
40
|
HumanInputSurface,
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { OpenGeniClient, WorkspaceArtifact, WorkspaceArtifactContentResponse, WorkspaceArtifactDetailResponse } from "@opengeni/sdk";
|
|
2
|
+
import { type PublishedHtmlArtifactToolBridge } from "./components/artifacts/published-html-artifact-frame.js";
|
|
3
|
+
/** Implement with an authenticated host proxy. Never put an organization key
|
|
4
|
+
* in browser props; replace the client object when the host actor changes. */
|
|
5
|
+
export type SiteClient = Pick<OpenGeniClient, "listWorkspaceArtifacts" | "getWorkspaceArtifact" | "getWorkspaceArtifactHtml" | "rollbackWorkspaceArtifact" | "setWorkspaceArtifactStatus">;
|
|
6
|
+
type SiteDisplayContent = Pick<WorkspaceArtifactContentResponse, "artifactId" | "versionId" | "html" | "requestedTools">;
|
|
7
|
+
type SiteScope = {
|
|
8
|
+
client: SiteClient;
|
|
9
|
+
workspaceId: string;
|
|
10
|
+
className?: string;
|
|
11
|
+
};
|
|
12
|
+
/** Shared native/embedded read boundary: pin content to the observed version,
|
|
13
|
+
* and never accept a response belonging to another workspace or Site. */
|
|
14
|
+
export declare function loadSiteSnapshot(client: Pick<SiteClient, "getWorkspaceArtifact" | "getWorkspaceArtifactHtml">, workspaceId: string, siteId: string, options?: {
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
includeArchivedContent?: boolean;
|
|
17
|
+
}): Promise<{
|
|
18
|
+
detail: WorkspaceArtifactDetailResponse;
|
|
19
|
+
content: SiteDisplayContent | null;
|
|
20
|
+
}>;
|
|
21
|
+
export type SiteListProps = SiteScope & {
|
|
22
|
+
status?: "active" | "archived";
|
|
23
|
+
onOpen: (site: WorkspaceArtifact) => void;
|
|
24
|
+
};
|
|
25
|
+
export type SiteDetailProps = SiteScope & {
|
|
26
|
+
siteId: string;
|
|
27
|
+
/** Presentation hint only: the backend still enforces artifacts:publish. */
|
|
28
|
+
canPublish?: boolean;
|
|
29
|
+
toolBridge?: PublishedHtmlArtifactToolBridge;
|
|
30
|
+
};
|
|
31
|
+
export declare function SiteList(props: SiteListProps): import("react").JSX.Element;
|
|
32
|
+
export declare function SiteDetail(props: SiteDetailProps): import("react").JSX.Element;
|
|
33
|
+
export {};
|
package/dist/sites.d.ts
ADDED
package/dist/sites.js
ADDED
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PublishedHtmlArtifactFrame
|
|
3
|
+
} from "./chunk-BRFMGY4L.js";
|
|
4
|
+
|
|
5
|
+
// src/sites-ui.tsx
|
|
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 versionId = detail.artifact.currentVersion?.id;
|
|
15
|
+
const content = versionId && (detail.artifact.status === "active" || options.includeArchivedContent) ? {
|
|
16
|
+
artifactId: siteId,
|
|
17
|
+
versionId,
|
|
18
|
+
requestedTools: detail.artifact.currentVersion.requestedTools,
|
|
19
|
+
html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {
|
|
20
|
+
...requestOptions,
|
|
21
|
+
versionId
|
|
22
|
+
})
|
|
23
|
+
} : null;
|
|
24
|
+
options.signal?.throwIfAborted();
|
|
25
|
+
if (content && (content.artifactId !== siteId || content.versionId !== versionId))
|
|
26
|
+
throw new Error("Site content mismatch");
|
|
27
|
+
return { detail: structuredClone(detail), content: content ? structuredClone(content) : null };
|
|
28
|
+
}
|
|
29
|
+
function useScopeKey(values) {
|
|
30
|
+
const [scope, setScope] = useState(values);
|
|
31
|
+
const [generation, setGeneration] = useState(0);
|
|
32
|
+
if (values.some((value, index) => value !== scope[index])) {
|
|
33
|
+
setScope(values);
|
|
34
|
+
setGeneration(generation + 1);
|
|
35
|
+
}
|
|
36
|
+
return generation;
|
|
37
|
+
}
|
|
38
|
+
function SiteList(props) {
|
|
39
|
+
const key = useScopeKey([props.client, props.workspaceId, props.status]);
|
|
40
|
+
return /* @__PURE__ */ jsx(ScopedSiteList, { ...props }, key);
|
|
41
|
+
}
|
|
42
|
+
function ScopedSiteList({
|
|
43
|
+
client,
|
|
44
|
+
workspaceId,
|
|
45
|
+
status = "active",
|
|
46
|
+
onOpen,
|
|
47
|
+
className
|
|
48
|
+
}) {
|
|
49
|
+
const [page, setPage] = useState(null);
|
|
50
|
+
const [cursor, setCursor] = useState();
|
|
51
|
+
const [error, setError] = useState(false);
|
|
52
|
+
const [reload, setReload] = useState(0);
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
const abort = new AbortController();
|
|
55
|
+
setPage(null);
|
|
56
|
+
setError(false);
|
|
57
|
+
void Promise.resolve().then(
|
|
58
|
+
() => client.listWorkspaceArtifacts(workspaceId, {
|
|
59
|
+
status,
|
|
60
|
+
...cursor ? { cursor } : {},
|
|
61
|
+
limit: 50,
|
|
62
|
+
signal: abort.signal
|
|
63
|
+
})
|
|
64
|
+
).then((result) => {
|
|
65
|
+
if (!abort.signal.aborted) setPage(structuredClone(result));
|
|
66
|
+
}).catch(() => {
|
|
67
|
+
if (!abort.signal.aborted) setError(true);
|
|
68
|
+
});
|
|
69
|
+
return () => abort.abort();
|
|
70
|
+
}, [client, workspaceId, status, cursor, reload]);
|
|
71
|
+
return /* @__PURE__ */ jsxs("section", { className, "aria-label": "Sites", "aria-busy": !page && !error, children: [
|
|
72
|
+
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: [
|
|
73
|
+
"No ",
|
|
74
|
+
status,
|
|
75
|
+
" Sites."
|
|
76
|
+
] }) : /* @__PURE__ */ jsx("ul", { children: page.artifacts.map((site) => /* @__PURE__ */ jsxs("li", { children: [
|
|
77
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => onOpen(structuredClone(site)), children: site.title }),
|
|
78
|
+
/* @__PURE__ */ jsx("p", { children: site.description }),
|
|
79
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
80
|
+
site.status,
|
|
81
|
+
" \xB7 revision ",
|
|
82
|
+
site.currentVersion?.revision ?? "unpublished"
|
|
83
|
+
] })
|
|
84
|
+
] }, site.id)) }),
|
|
85
|
+
page?.nextCursor && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCursor(page.nextCursor), children: "Next page" }),
|
|
86
|
+
cursor && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setCursor(void 0), children: "First page" }),
|
|
87
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setReload((value) => value + 1), children: "Refresh Sites" })
|
|
88
|
+
] });
|
|
89
|
+
}
|
|
90
|
+
function SiteDetail(props) {
|
|
91
|
+
const key = useScopeKey([props.client, props.workspaceId, props.siteId]);
|
|
92
|
+
return /* @__PURE__ */ jsx(ScopedSiteDetail, { ...props }, key);
|
|
93
|
+
}
|
|
94
|
+
function ScopedSiteDetail({
|
|
95
|
+
client,
|
|
96
|
+
workspaceId,
|
|
97
|
+
siteId,
|
|
98
|
+
canPublish = false,
|
|
99
|
+
toolBridge,
|
|
100
|
+
className
|
|
101
|
+
}) {
|
|
102
|
+
const [loaded, setLoaded] = useState(null);
|
|
103
|
+
const [error, setError] = useState(false);
|
|
104
|
+
const [reload, setReload] = useState(0);
|
|
105
|
+
const [busy, setBusy] = useState(false);
|
|
106
|
+
const [rollbackId, setRollbackId] = useState("");
|
|
107
|
+
const [confirmation, setConfirmation] = useState(null);
|
|
108
|
+
const mutation = useRef(null);
|
|
109
|
+
useEffect(() => () => mutation.current?.abort(), []);
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
const abort = new AbortController();
|
|
112
|
+
setLoaded(null);
|
|
113
|
+
setError(false);
|
|
114
|
+
setConfirmation(null);
|
|
115
|
+
setRollbackId("");
|
|
116
|
+
void Promise.resolve().then(async () => {
|
|
117
|
+
const snapshot = await loadSiteSnapshot(client, workspaceId, siteId, {
|
|
118
|
+
signal: abort.signal
|
|
119
|
+
});
|
|
120
|
+
if (!abort.signal.aborted) setLoaded(snapshot);
|
|
121
|
+
}).catch(() => {
|
|
122
|
+
if (!abort.signal.aborted) {
|
|
123
|
+
setLoaded(null);
|
|
124
|
+
setError(true);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
return () => abort.abort();
|
|
128
|
+
}, [client, workspaceId, siteId, reload]);
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
if (!loaded || busy) return;
|
|
131
|
+
const abort = new AbortController();
|
|
132
|
+
let checking = false;
|
|
133
|
+
const timer = setInterval(() => {
|
|
134
|
+
if (checking) return;
|
|
135
|
+
checking = true;
|
|
136
|
+
void Promise.resolve().then(() => client.getWorkspaceArtifact(workspaceId, siteId, { signal: abort.signal })).then((detail) => {
|
|
137
|
+
if (abort.signal.aborted) return;
|
|
138
|
+
if (detail.artifact.id !== siteId || detail.artifact.workspaceId !== workspaceId)
|
|
139
|
+
throw new Error("Site scope mismatch");
|
|
140
|
+
if (detail.artifact.currentVersion?.id !== loaded.detail.artifact.currentVersion?.id || detail.artifact.status !== loaded.detail.artifact.status) {
|
|
141
|
+
setLoaded(null);
|
|
142
|
+
setReload((value) => value + 1);
|
|
143
|
+
}
|
|
144
|
+
}).catch(() => {
|
|
145
|
+
if (!abort.signal.aborted) {
|
|
146
|
+
setLoaded(null);
|
|
147
|
+
setError(true);
|
|
148
|
+
}
|
|
149
|
+
}).finally(() => {
|
|
150
|
+
checking = false;
|
|
151
|
+
});
|
|
152
|
+
}, 15e3);
|
|
153
|
+
return () => {
|
|
154
|
+
abort.abort();
|
|
155
|
+
clearInterval(timer);
|
|
156
|
+
};
|
|
157
|
+
}, [client, workspaceId, siteId, loaded, busy]);
|
|
158
|
+
const commit = async () => {
|
|
159
|
+
const current = loaded?.detail.artifact.currentVersion;
|
|
160
|
+
if (!canPublish || !loaded || !current || !confirmation || mutation.current) return;
|
|
161
|
+
const abort = new AbortController();
|
|
162
|
+
mutation.current = abort;
|
|
163
|
+
setBusy(true);
|
|
164
|
+
try {
|
|
165
|
+
const common = { expectedCurrentVersionId: current.id, idempotencyKey: crypto.randomUUID() };
|
|
166
|
+
if (confirmation === "rollback") {
|
|
167
|
+
if (!loaded.detail.versions.some((version) => version.id === rollbackId))
|
|
168
|
+
throw new Error("Unknown version");
|
|
169
|
+
await client.rollbackWorkspaceArtifact(
|
|
170
|
+
workspaceId,
|
|
171
|
+
siteId,
|
|
172
|
+
{ ...common, versionId: rollbackId, reason: "Restore explicitly selected Site version" },
|
|
173
|
+
{ signal: abort.signal }
|
|
174
|
+
);
|
|
175
|
+
} else {
|
|
176
|
+
await client.setWorkspaceArtifactStatus(
|
|
177
|
+
workspaceId,
|
|
178
|
+
siteId,
|
|
179
|
+
{
|
|
180
|
+
...common,
|
|
181
|
+
status: loaded.detail.artifact.status === "active" ? "archived" : "active",
|
|
182
|
+
reason: "Explicit Site status change"
|
|
183
|
+
},
|
|
184
|
+
{ signal: abort.signal }
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
if (!abort.signal.aborted) {
|
|
188
|
+
setLoaded(null);
|
|
189
|
+
setReload((value) => value + 1);
|
|
190
|
+
}
|
|
191
|
+
} catch {
|
|
192
|
+
if (!abort.signal.aborted) {
|
|
193
|
+
setLoaded(null);
|
|
194
|
+
setError(true);
|
|
195
|
+
}
|
|
196
|
+
} finally {
|
|
197
|
+
if (!abort.signal.aborted) {
|
|
198
|
+
mutation.current = null;
|
|
199
|
+
setBusy(false);
|
|
200
|
+
setConfirmation(null);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
const site = loaded?.detail.artifact;
|
|
205
|
+
return /* @__PURE__ */ jsxs(
|
|
206
|
+
"section",
|
|
207
|
+
{
|
|
208
|
+
className,
|
|
209
|
+
"aria-label": "Site details",
|
|
210
|
+
"aria-busy": busy || !loaded && !error,
|
|
211
|
+
children: [
|
|
212
|
+
error && /* @__PURE__ */ jsx("p", { role: "alert", children: "Site state could not be confirmed or access has changed. Refresh before continuing." }),
|
|
213
|
+
!loaded && !error && /* @__PURE__ */ jsx("p", { role: "status", children: "Loading Site\u2026" }),
|
|
214
|
+
site && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
215
|
+
/* @__PURE__ */ jsx("h2", { children: site.title }),
|
|
216
|
+
/* @__PURE__ */ jsx("p", { children: site.description }),
|
|
217
|
+
/* @__PURE__ */ jsxs("p", { children: [
|
|
218
|
+
site.status,
|
|
219
|
+
" \xB7 revision ",
|
|
220
|
+
site.currentVersion?.revision ?? "unpublished"
|
|
221
|
+
] }),
|
|
222
|
+
loaded.content && /* @__PURE__ */ jsx(
|
|
223
|
+
PublishedHtmlArtifactFrame,
|
|
224
|
+
{
|
|
225
|
+
html: loaded.content.html,
|
|
226
|
+
title: site.title,
|
|
227
|
+
...toolBridge ? { toolBridge } : {}
|
|
228
|
+
}
|
|
229
|
+
),
|
|
230
|
+
/* @__PURE__ */ jsx("h3", { children: "Versions" }),
|
|
231
|
+
/* @__PURE__ */ jsx("ul", { children: loaded.detail.versions.map((version) => /* @__PURE__ */ jsxs("li", { children: [
|
|
232
|
+
"Revision ",
|
|
233
|
+
version.revision,
|
|
234
|
+
" \xB7 ",
|
|
235
|
+
version.createdAt,
|
|
236
|
+
version.id === site.currentVersion?.id ? " \xB7 current" : ""
|
|
237
|
+
] }, version.id)) }),
|
|
238
|
+
loaded.detail.versionsTruncated && /* @__PURE__ */ jsx("p", { children: "Older versions are not included in this response." }),
|
|
239
|
+
canPublish && site.currentVersion && /* @__PURE__ */ jsxs("fieldset", { disabled: busy, children: [
|
|
240
|
+
/* @__PURE__ */ jsx("legend", { children: "Manage Site" }),
|
|
241
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmation("status"), children: site.status === "active" ? "Archive Site" : "Restore Site" }),
|
|
242
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
243
|
+
"Version to restore",
|
|
244
|
+
/* @__PURE__ */ jsxs(
|
|
245
|
+
"select",
|
|
246
|
+
{
|
|
247
|
+
value: rollbackId,
|
|
248
|
+
onChange: (event) => {
|
|
249
|
+
setRollbackId(event.target.value);
|
|
250
|
+
setConfirmation(null);
|
|
251
|
+
},
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsx("option", { value: "", children: "Choose a version" }),
|
|
254
|
+
loaded.detail.versions.filter((version) => version.id !== site.currentVersion?.id).map((version) => /* @__PURE__ */ jsxs("option", { value: version.id, children: [
|
|
255
|
+
"Revision ",
|
|
256
|
+
version.revision
|
|
257
|
+
] }, version.id))
|
|
258
|
+
]
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
] }),
|
|
262
|
+
/* @__PURE__ */ jsx(
|
|
263
|
+
"button",
|
|
264
|
+
{
|
|
265
|
+
type: "button",
|
|
266
|
+
disabled: !rollbackId,
|
|
267
|
+
onClick: () => setConfirmation("rollback"),
|
|
268
|
+
children: "Review rollback"
|
|
269
|
+
}
|
|
270
|
+
),
|
|
271
|
+
confirmation && /* @__PURE__ */ jsxs("div", { role: "group", "aria-label": "Confirm Site change", children: [
|
|
272
|
+
/* @__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?" }),
|
|
273
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => void commit(), children: "Confirm change" }),
|
|
274
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmation(null), children: "Keep current state" })
|
|
275
|
+
] })
|
|
276
|
+
] })
|
|
277
|
+
] }),
|
|
278
|
+
/* @__PURE__ */ jsx("button", { type: "button", disabled: busy, onClick: () => setReload((value) => value + 1), children: "Refresh Site" })
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
export {
|
|
284
|
+
SiteDetail,
|
|
285
|
+
SiteList,
|
|
286
|
+
loadSiteSnapshot
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=sites.js.map
|
|
@@ -0,0 +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 workspaceId: string,\n siteId: string,\n options: { signal?: AbortSignal; includeArchivedContent?: boolean } = {},\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 versionId = detail.artifact.currentVersion?.id;\n const content =\n versionId && (detail.artifact.status === \"active\" || options.includeArchivedContent)\n ? {\n artifactId: siteId,\n versionId,\n requestedTools: detail.artifact.currentVersion!.requestedTools,\n html: await client.getWorkspaceArtifactHtml(workspaceId, siteId, {\n ...requestOptions,\n versionId,\n }),\n }\n : null;\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":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "5.0.4-canary.0",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -21,6 +21,19 @@
|
|
|
21
21
|
"module": "./dist/index.js",
|
|
22
22
|
"types": "./dist/index.d.ts",
|
|
23
23
|
"exports": {
|
|
24
|
+
"./sites": {
|
|
25
|
+
"types": "./dist/sites.d.ts",
|
|
26
|
+
"import": "./dist/sites.js"
|
|
27
|
+
},
|
|
28
|
+
"./connect.css": {
|
|
29
|
+
"types": "./styles/connect.d.ts",
|
|
30
|
+
"style": "./styles/connect.css",
|
|
31
|
+
"default": "./styles/connect.css"
|
|
32
|
+
},
|
|
33
|
+
"./connect": {
|
|
34
|
+
"types": "./dist/connect.d.ts",
|
|
35
|
+
"import": "./dist/connect.js"
|
|
36
|
+
},
|
|
24
37
|
".": {
|
|
25
38
|
"types": "./dist/index.d.ts",
|
|
26
39
|
"import": "./dist/index.js"
|
|
@@ -120,7 +133,8 @@
|
|
|
120
133
|
"@dnd-kit/utilities": "3.2.2",
|
|
121
134
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
122
135
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
123
|
-
"@opengeni/
|
|
136
|
+
"@opengeni/connect": "^0.2.0-canary.1",
|
|
137
|
+
"@opengeni/sdk": "^5.0.3-canary.0",
|
|
124
138
|
"clsx": "^2.1.1",
|
|
125
139
|
"dequal": "2.0.3",
|
|
126
140
|
"lucide-react": "^1.8.0",
|
|
@@ -78,8 +78,11 @@ export function observeMarkdownTableLayout(wrapper: HTMLDivElement, table: HTMLT
|
|
|
78
78
|
observer?.observe(scroller);
|
|
79
79
|
observer?.observe(body);
|
|
80
80
|
observer?.observe(table);
|
|
81
|
-
return
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
return {
|
|
82
|
+
measure,
|
|
83
|
+
disconnect: () => {
|
|
84
|
+
observer?.disconnect();
|
|
85
|
+
reset();
|
|
86
|
+
},
|
|
84
87
|
};
|
|
85
88
|
}
|
|
@@ -21,6 +21,7 @@ import { tableElementToTsv } from "../lib/clipboard";
|
|
|
21
21
|
import { prefersReducedMotion } from "../lib/motion";
|
|
22
22
|
import { MOTION_INSPECT_SCALE } from "../lib/motion-inspect";
|
|
23
23
|
import { CopyButton } from "./copy-button";
|
|
24
|
+
import type { observeMarkdownTableLayout } from "./markdown-table-layout";
|
|
24
25
|
import { softenStreamingMarkdown } from "./soften-streaming-markdown";
|
|
25
26
|
import { createStreamReveal, rehypeStreamReveal, type StreamReveal } from "./stream-reveal";
|
|
26
27
|
import { TooltipProvider } from "./tooltip";
|
|
@@ -413,22 +414,29 @@ function MarkdownCodeBlock({ children }: { children?: ReactNode }) {
|
|
|
413
414
|
function MarkdownTable({ children, className, ...props }: ComponentPropsWithoutRef<"table">) {
|
|
414
415
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
415
416
|
const tableRef = useRef<HTMLTableElement>(null);
|
|
417
|
+
const layoutRef = useRef<ReturnType<typeof observeMarkdownTableLayout>>(undefined);
|
|
416
418
|
useEffect(() => {
|
|
417
419
|
const wrapper = wrapperRef.current;
|
|
418
420
|
const table = tableRef.current;
|
|
419
421
|
if (!wrapper?.closest("[data-og-wide-table-message]") || !table) return;
|
|
420
422
|
let disposed = false;
|
|
421
|
-
let cleanup: (() => void) | undefined;
|
|
422
423
|
// Ordinary tables remain usable even if this optional layout chunk fails.
|
|
423
424
|
void import("./markdown-table-layout")
|
|
424
425
|
.then(({ observeMarkdownTableLayout }) => {
|
|
425
|
-
if (!disposed)
|
|
426
|
+
if (!disposed) layoutRef.current = observeMarkdownTableLayout(wrapper, table);
|
|
426
427
|
})
|
|
427
428
|
.catch(() => {});
|
|
428
429
|
return () => {
|
|
429
430
|
disposed = true;
|
|
430
|
-
|
|
431
|
+
layoutRef.current?.disconnect();
|
|
432
|
+
layoutRef.current = undefined;
|
|
431
433
|
};
|
|
434
|
+
}, []);
|
|
435
|
+
// ReactMarkdown recreates table children even when only later prose changes.
|
|
436
|
+
// Remeasure genuine content changes without collapsing the expanded wrapper
|
|
437
|
+
// between cleanup and the asynchronous observer-module import on each update.
|
|
438
|
+
useEffect(() => {
|
|
439
|
+
layoutRef.current?.measure();
|
|
432
440
|
}, [children]);
|
|
433
441
|
return (
|
|
434
442
|
<div ref={wrapperRef} className="group/copy relative mt-3 max-w-full first:mt-0">
|