camox 0.56.0 → 0.58.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/dist/core/createApp.d.ts +2 -2
- package/dist/core/createBlock.js +6 -2
- package/dist/core/createLayout.d.ts +1 -1
- package/dist/core/editing/createEditableBlock.d.ts +1 -1
- package/dist/core/lib/artifact.d.ts +14 -4
- package/dist/core/lib/artifact.js +57 -16
- package/dist/core/lib/artifactRuntime.js +4 -2
- package/dist/features/canvas/CanvasPageHeader.js +1 -1
- package/dist/features/canvas/canvasFrameDocument.js +13 -6
- package/dist/features/preview/components/Frame.js +1 -18
- package/dist/features/preview/components/SidebarStack.js +193 -106
- package/dist/features/preview/components/previewStyles.js +25 -1
- package/dist/features/renderer/RendererApp.js +55 -37
- package/dist/features/runtime/artifactDocuments.js +58 -0
- package/dist/features/runtime/artifactSources.js +36 -2
- package/dist/features/runtime/runtime.js +30 -12
- package/dist/features/runtime/runtimeRoutes.d.ts +6 -5
- package/dist/features/runtime/runtimeRoutes.js +12 -5
- package/dist/features/studio/components/AgentSidebar.js +4 -4
- package/dist/features/studio/components/ChatWidget.js +62 -80
- package/dist/features/studio/components/DraftsControl.js +60 -134
- package/dist/features/studio/components/Patrol.js +676 -0
- package/dist/features/studio/components/ReviewDraftSheet.js +52 -4
- package/dist/features/studio/components/Squareling.js +169 -84
- package/dist/features/studio/components/ThreadMessages.js +155 -58
- package/dist/features/studio/localeOptions.js +7 -3
- package/dist/features/studio/reportSentence.js +75 -0
- package/dist/features/studio/routes.js +2 -1
- package/dist/features/studio/showInCanvas.js +9 -2
- package/dist/features/studio/workingSentence.js +55 -0
- package/dist/lib/studio-reads.d.ts +1 -0
- package/dist/lib/studio-reads.js +5 -0
- package/dist/lib/use-thread.js +156 -190
- package/dist/package.js +1 -1
- package/dist/studio.css +1 -1
- package/package.json +4 -4
- package/dist/features/studio/components/RenameDraftDialog.js +0 -206
- package/dist/features/studio/components/WorkersWidget.js +0 -52
package/dist/core/createApp.d.ts
CHANGED
|
@@ -416,7 +416,7 @@ export declare function createApp({ blocks, lazyBlocks, layouts, collections, do
|
|
|
416
416
|
Component: ({ blockData, mode, isFirstBlock, showAddBlockTop, showAddBlockBottom, addBlockAfterPosition }: BlockComponentProps<{
|
|
417
417
|
[x: string]: unknown;
|
|
418
418
|
}>) => import("react").JSX.Element;
|
|
419
|
-
LayoutItem: ({ collectionId, collection, properties, record, children, fallback }: LayoutItemProps) => string | number | bigint | boolean | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> |
|
|
419
|
+
LayoutItem: ({ collectionId, collection, properties, record, children, fallback }: LayoutItemProps) => string | number | bigint | boolean | import("react").JSX.Element | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | null | undefined;
|
|
420
420
|
id: string;
|
|
421
421
|
title: string;
|
|
422
422
|
description: string;
|
|
@@ -858,7 +858,7 @@ export declare function createApp({ blocks, lazyBlocks, layouts, collections, do
|
|
|
858
858
|
Component: ({ blockData, mode, isFirstBlock, showAddBlockTop, showAddBlockBottom, addBlockAfterPosition }: BlockComponentProps<{
|
|
859
859
|
[x: string]: unknown;
|
|
860
860
|
}>) => import("react").JSX.Element;
|
|
861
|
-
LayoutItem: ({ collectionId, collection, properties, record, children, fallback }: LayoutItemProps) => string | number | bigint | boolean | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> |
|
|
861
|
+
LayoutItem: ({ collectionId, collection, properties, record, children, fallback }: LayoutItemProps) => string | number | bigint | boolean | import("react").JSX.Element | Iterable<import("react").ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<import("react").ReactNode> | null | undefined> | null | undefined;
|
|
862
862
|
id: string;
|
|
863
863
|
title: string;
|
|
864
864
|
description: string;
|
package/dist/core/createBlock.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { artifactFrameProps, artifactStandInProps, isArtifactSource } from "./lib/artifact.js";
|
|
1
|
+
import { artifactFrameProps, artifactStandInProps, isArtifactSource, isArtifactSourceReference } from "./lib/artifact.js";
|
|
2
2
|
import { artifactData } from "./lib/artifactData.js";
|
|
3
3
|
import { resolveBlockFields, resolveToMarkdown, resolveToStructuredData, resolveToTitle } from "./lib/contentType.js";
|
|
4
4
|
import { useProjectUuid } from "../lib/auth.js";
|
|
@@ -190,9 +190,13 @@ function createViewBlock(options) {
|
|
|
190
190
|
size: useFieldSchema(name)?.size,
|
|
191
191
|
navigate: useNavigate()
|
|
192
192
|
});
|
|
193
|
-
const initialData = useInitialArtifactData(value, data);
|
|
193
|
+
const initialData = useInitialArtifactData(isArtifactSourceReference(value) ? value.artifactUrl : value, data);
|
|
194
194
|
const fontLinks = useOptionalCamoxApp()?.getArtifactFontLinks();
|
|
195
195
|
const runs = React.use(ArtifactsRunContext);
|
|
196
|
+
if (isArtifactSourceReference(value)) return children({
|
|
197
|
+
...artifactFrameProps(value, { data: initialData }),
|
|
198
|
+
...hostProps
|
|
199
|
+
});
|
|
196
200
|
if (!isArtifactSource(value)) return null;
|
|
197
201
|
return children({
|
|
198
202
|
...runs ? artifactFrameProps(value, {
|
|
@@ -236,7 +236,7 @@ declare function createLayoutDefinition(options: AnyLayoutOptions): {
|
|
|
236
236
|
Item: {
|
|
237
237
|
({ children }: {
|
|
238
238
|
children: (item: any) => React.ReactNode;
|
|
239
|
-
}): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> |
|
|
239
|
+
}): string | number | bigint | boolean | React.JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined;
|
|
240
240
|
displayName: string;
|
|
241
241
|
};
|
|
242
242
|
useData(): unknown;
|
|
@@ -1694,7 +1694,7 @@ export declare function createEditableBlock<TSchemaShape extends Record<string,
|
|
|
1694
1694
|
params: unknown[] & [];
|
|
1695
1695
|
})["static"]; }, Exclude<keyof TSchemaShape, { [K_1 in keyof TSchemaShape]: TSchemaShape[K_1] extends import("@sinclair/typebox").TReadonly<TSchema> ? TSchemaShape[K_1] extends import("@sinclair/typebox").TOptional<TSchemaShape[K_1]> ? K_1 : never : never; }[keyof TSchemaShape] | { [K_2 in keyof TSchemaShape]: TSchemaShape[K_2] extends import("@sinclair/typebox").TReadonly<TSchema> ? TSchemaShape[K_2] extends import("@sinclair/typebox").TOptional<TSchemaShape[K_2]> ? never : K_2 : never; }[keyof TSchemaShape] | { [K_3 in keyof TSchemaShape]: TSchemaShape[K_3] extends import("@sinclair/typebox").TOptional<TSchema> ? TSchemaShape[K_3] extends import("@sinclair/typebox").TReadonly<TSchemaShape[K_3]> ? never : K_3 : never; }[keyof TSchemaShape]>>>>>) => React.JSX.Element;
|
|
1696
1696
|
/** Renders a record page's record; see `BlockEditingRuntime.renderLayoutItem`. */
|
|
1697
|
-
LayoutItem: ({ collectionId, collection, properties, record, children, fallback }: LayoutItemProps) => string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> |
|
|
1697
|
+
LayoutItem: ({ collectionId, collection, properties, record, children, fallback }: LayoutItemProps) => string | number | bigint | boolean | React.JSX.Element | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode> | null | undefined> | null | undefined;
|
|
1698
1698
|
id: string;
|
|
1699
1699
|
title: string;
|
|
1700
1700
|
description: string;
|
|
@@ -3,14 +3,23 @@ import "./artifactData.js";
|
|
|
3
3
|
import * as React from "react";
|
|
4
4
|
import "@camox/api-contract";
|
|
5
5
|
//#region src/core/lib/artifact.d.ts
|
|
6
|
-
/** The frame attributes
|
|
7
|
-
|
|
8
|
-
srcDoc: string;
|
|
6
|
+
/** The frame attributes an artifact's frame has, whichever way it loads its document. */
|
|
7
|
+
type ArtifactFrameAttributes = {
|
|
9
8
|
sandbox: string;
|
|
10
9
|
allow: string;
|
|
11
10
|
title: string;
|
|
12
11
|
loading: "lazy";
|
|
13
12
|
};
|
|
13
|
+
/** The frame attributes of an artifact whose document is inlined. */
|
|
14
|
+
export type InlineArtifactFrameProps = ArtifactFrameAttributes & {
|
|
15
|
+
srcDoc: string;
|
|
16
|
+
src?: never;
|
|
17
|
+
};
|
|
18
|
+
/** The frame attributes of an artifact: its document inlined, or loaded by URL. */
|
|
19
|
+
export type ArtifactFrameProps = InlineArtifactFrameProps | (ArtifactFrameAttributes & {
|
|
20
|
+
src: string;
|
|
21
|
+
srcDoc?: never;
|
|
22
|
+
});
|
|
14
23
|
/** What `block.Artifact` passes to its render prop, to spread onto an `<iframe>`. */
|
|
15
24
|
export type ArtifactRenderProps = ArtifactFrameProps & {
|
|
16
25
|
/** Connects the frame to the `camox` runtime inside it. */
|
|
@@ -18,4 +27,5 @@ export type ArtifactRenderProps = ArtifactFrameProps & {
|
|
|
18
27
|
/** For `size: "content"`, the height the artifact's content needs, once it reports it. */
|
|
19
28
|
style?: React.CSSProperties;
|
|
20
29
|
};
|
|
21
|
-
//#endregion
|
|
30
|
+
//#endregion
|
|
31
|
+
export {};
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { escapeAttribute } from "../../lib/html.js";
|
|
2
2
|
import { ARTIFACT_RUNTIME } from "./artifactRuntime.js";
|
|
3
|
-
import { artifactTitle } from "@camox/api-contract";
|
|
3
|
+
import { artifactSourceHash, artifactTitle } from "@camox/api-contract";
|
|
4
4
|
|
|
5
5
|
//#region src/core/lib/artifact.ts
|
|
6
|
+
/** Whether a stored artifact value was replaced by a reference to its source. */
|
|
7
|
+
function isArtifactSourceReference(value) {
|
|
8
|
+
if (typeof value !== "object" || value === null) return false;
|
|
9
|
+
const { artifactUrl, title } = value;
|
|
10
|
+
return typeof artifactUrl === "string" && typeof title === "string";
|
|
11
|
+
}
|
|
6
12
|
const FONT_LINK_RELS = /* @__PURE__ */ new Set(["stylesheet", "preconnect"]);
|
|
7
13
|
const UNHEAD_TAG_OPTIONS = /* @__PURE__ */ new Set([
|
|
8
14
|
"key",
|
|
@@ -32,6 +38,15 @@ function artifactDataScript(data) {
|
|
|
32
38
|
return `${DATA_SCRIPT_OPEN}${json}<\/script>`;
|
|
33
39
|
}
|
|
34
40
|
/**
|
|
41
|
+
* The fragment, `#` included, that carries the starting Artifact Data in the URL of a frame that
|
|
42
|
+
* loads its artifact by URL instead of `srcdoc`. The runtime decodes it as `camox.data`. Fragments
|
|
43
|
+
* never reach the server, so one cached document serves every placement. Empty data needs none.
|
|
44
|
+
*/
|
|
45
|
+
function artifactDataFragment(data) {
|
|
46
|
+
const json = JSON.stringify(data);
|
|
47
|
+
return json === "{}" ? "" : `#${encodeURIComponent(json)}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
35
50
|
* The stylesheet and preconnect links of the project's document definition, as HTML, so artifacts
|
|
36
51
|
* use the website's fonts from their first render. Nothing else from the document's head: its
|
|
37
52
|
* title, meta, scripts and icons belong to the website, not to the artifact.
|
|
@@ -47,12 +62,35 @@ function artifactFontLinks(document) {
|
|
|
47
62
|
}).map(renderLink).join("");
|
|
48
63
|
}
|
|
49
64
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
65
|
+
* What every artifact's document holds before its source: the `camox` runtime, then the website's
|
|
66
|
+
* font links. The doctype comes first so the runtime's script never puts the page in quirks mode;
|
|
67
|
+
* the source's own doctype, if any, is then ignored by the parser.
|
|
68
|
+
*/
|
|
69
|
+
function artifactShell(fontLinks) {
|
|
70
|
+
return `<!doctype html><script>${ARTIFACT_RUNTIME}<\/script>${fontLinks}`;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* The hash of the shell around every artifact source, which names it in the URLs public pages
|
|
74
|
+
* load artifacts from. It changes only with the runtime or the website's font links, so a release
|
|
75
|
+
* that changes neither keeps every artifact cached.
|
|
76
|
+
*/
|
|
77
|
+
function artifactShellHash(fontLinks) {
|
|
78
|
+
return artifactSourceHash(artifactShell(fontLinks));
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The document an inline (`srcdoc`) artifact frame loads: the shell, the initial Artifact Data,
|
|
82
|
+
* then the stored source.
|
|
53
83
|
*/
|
|
54
84
|
function artifactDocument(source, { fontLinks = "", data = {} }) {
|
|
55
|
-
return
|
|
85
|
+
return `${artifactShell(fontLinks)}${artifactDataScript(data)}${source}`;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The document served at an artifact's URL: the shell, then the stored source. No Artifact Data:
|
|
89
|
+
* the frame's URL fragment carries it (`artifactDataFragment`), so one cached document serves
|
|
90
|
+
* every placement, and an inline data script would win over the fragment.
|
|
91
|
+
*/
|
|
92
|
+
function artifactUrlDocument(source, fontLinks) {
|
|
93
|
+
return `${artifactShell(fontLinks)}${source}`;
|
|
56
94
|
}
|
|
57
95
|
/**
|
|
58
96
|
* The artifact source inside an artifact's document, the reverse of `artifactDocument`, or `null`
|
|
@@ -60,7 +98,7 @@ function artifactDocument(source, { fontLinks = "", data = {} }) {
|
|
|
60
98
|
* JSON escapes it too, so the first `<\/script>` after the data's opening tag ends the data.
|
|
61
99
|
*/
|
|
62
100
|
function artifactSourceOf(document) {
|
|
63
|
-
const head =
|
|
101
|
+
const head = artifactShell("");
|
|
64
102
|
if (!document.startsWith(head)) return null;
|
|
65
103
|
const data = document.indexOf(DATA_SCRIPT_OPEN, head.length);
|
|
66
104
|
if (data < 0 || document.slice(head.length, data).includes("<script")) return null;
|
|
@@ -68,19 +106,22 @@ function artifactSourceOf(document) {
|
|
|
68
106
|
if (end < 0) return null;
|
|
69
107
|
return document.slice(end + 9);
|
|
70
108
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
* links. This getter is the boundary that hides how the source reaches the frame (inline `srcdoc`
|
|
74
|
-
* today), so blocks never depend on it.
|
|
75
|
-
*/
|
|
76
|
-
function artifactFrameProps(source, options = {}) {
|
|
77
|
-
return {
|
|
78
|
-
srcDoc: artifactDocument(source, options),
|
|
109
|
+
function artifactFrameProps(artifact, options = {}) {
|
|
110
|
+
const attributes = {
|
|
79
111
|
sandbox: "allow-scripts",
|
|
80
112
|
allow: "fullscreen; clipboard-write",
|
|
81
|
-
title: artifactTitle(source),
|
|
82
113
|
loading: "lazy"
|
|
83
114
|
};
|
|
115
|
+
if (typeof artifact === "string") return {
|
|
116
|
+
srcDoc: artifactDocument(artifact, options),
|
|
117
|
+
...attributes,
|
|
118
|
+
title: artifactTitle(artifact)
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
src: `${artifact.artifactUrl}${artifactDataFragment(options.data ?? {})}`,
|
|
122
|
+
...attributes,
|
|
123
|
+
title: artifact.title
|
|
124
|
+
};
|
|
84
125
|
}
|
|
85
126
|
const PLACEHOLDER_SOURCE = `<!doctype html>
|
|
86
127
|
<html>
|
|
@@ -151,4 +192,4 @@ function isArtifactSource(value) {
|
|
|
151
192
|
}
|
|
152
193
|
|
|
153
194
|
//#endregion
|
|
154
|
-
export { artifactFontLinks, artifactFrameProps, artifactPlaceholderProps, artifactSourceOf, artifactStandInProps, artifactTitle, isArtifactSource };
|
|
195
|
+
export { artifactDataFragment, artifactFontLinks, artifactFrameProps, artifactPlaceholderProps, artifactShellHash, artifactSourceOf, artifactStandInProps, artifactTitle, artifactUrlDocument, isArtifactSource, isArtifactSourceReference };
|
|
@@ -13,7 +13,9 @@
|
|
|
13
13
|
* change. The host converts the colors, so the runtime that every frame inlines stays small.
|
|
14
14
|
* - host → frame: `data` with the latest Artifact Data. The initial data is inlined in the
|
|
15
15
|
* `srcDoc` as a `<script type="application/json" data-camox-data>` after the runtime and the
|
|
16
|
-
* font links,
|
|
16
|
+
* font links, or, for a frame loaded by URL, encoded in the URL's fragment (see
|
|
17
|
+
* `artifactDataFragment`). It is read lazily, on first access, but the fragment is captured as
|
|
18
|
+
* the runtime starts, before the artifact source can change it.
|
|
17
19
|
* - runtime → page: `observe-viewport` with `observe`, when the artifact's first `onViewport`
|
|
18
20
|
* subscription starts or its last one ends. While observed, the host sends `viewport` with
|
|
19
21
|
* `progress` and `visibleRatio` whenever they change.
|
|
@@ -30,7 +32,7 @@
|
|
|
30
32
|
* `plugins/minifyArtifactRuntime.ts`), so it must stay one template literal without
|
|
31
33
|
* interpolations or backslashes.
|
|
32
34
|
*/
|
|
33
|
-
const ARTIFACT_RUNTIME = "(()=>{\"use strict\";let e=window.parent,t=null,n=null,r,i,a=new Set,o=()=>{if(i!==void 0)return r;let e=document.querySelector(`script[data-camox-data]`);try{r=
|
|
35
|
+
const ARTIFACT_RUNTIME = "(()=>{\"use strict\";let e=window.parent,t=null,n=null,r,i,a=new Set,o=location.hash.slice(1),s=()=>{if(i!==void 0)return r;let e=document.querySelector(`script[data-camox-data]`);try{r=JSON.parse(e?e.textContent:decodeURIComponent(o)),(!r||typeof r!=`object`||Array.isArray(r))&&(r={})}catch{r={}}return i=JSON.stringify(r),r},c=(()=>{let e=new Set,t=[],n=Object.freeze({tokens:Object.freeze({}),colors:Object.freeze({})}),r=document.createElement(`style`);r.textContent=`body { visibility: hidden !important; }`,document.head.append(r);let i=()=>r.remove();setTimeout(i,300);let a=e=>Number.isFinite(e)?Math.min(255,Math.max(0,Math.round(e))):0;return{get current(){return n},receive(r){let o={},s={},c=r.colors||{};for(let[e,t]of Object.entries(r.tokens||{})){if(!e.startsWith(`--`)||typeof t!=`string`)continue;o[e]=t;let n=Object.hasOwn(c,e)?c[e]:null;n&&typeof n==`object`&&(s[e]=Object.freeze({r:a(n.r),g:a(n.g),b:a(n.b)}))}let l=document.documentElement.style;for(let e of t)Object.hasOwn(o,e)||l.removeProperty(e);for(let[e,t]of Object.entries(o))l.setProperty(e,t);t=Object.keys(o),i(),n=Object.freeze({tokens:Object.freeze(o),colors:Object.freeze(s)});for(let t of[...e])t(n)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}})(),l=new Set,u=null,d=null,f=null,p=()=>{let e=Math.ceil(document.documentElement.getBoundingClientRect().height);e!==f&&(f=e,m(`height`,{height:f}))},m=(n,r)=>{t&&e.postMessage({...r,camox:n,channel:t},`*`)},h=()=>m(`observe-viewport`,{observe:l.size>0}),g={studio(e){let t=e.studio;n=t?Object.freeze({editing:!!t.editing,canvas:!!t.canvas}):null},data(e){s();let t=JSON.stringify(e.data??{});if(t!==i){r=JSON.parse(t),i=t;for(let e of[...a])e(r)}},theme:c.receive,measure(){f=null,d||(d=new ResizeObserver(p),d.observe(document.documentElement)),p()},viewport(e){let{progress:t,visibleRatio:n}=e;if(typeof t==`number`&&typeof n==`number`){u=Object.freeze({progress:t,visibleRatio:n}),document.documentElement.style.setProperty(`--camox-progress`,String(t));for(let e of l)e(u)}}};window.addEventListener(`message`,e=>{let n=e.data;if(n&&typeof n.camox==`string`){if(n.camox===`connect`){if(typeof n.channel!=`string`||n.channel===t)return;t=n.channel,m(`ready`),l.size>0&&h()}else t&&n.channel===t&&Object.hasOwn(g,n.camox)&&g[n.camox](n)}});let _=Object.freeze({get studio(){return n},get data(){return s()},onData(e){return a.add(e),()=>{a.delete(e)}},get theme(){return c.current},onTheme:c.subscribe,navigate(e){m(`navigate`,{href:String(e)})},onViewport(e){if(typeof e!=`function`)throw TypeError(`camox.onViewport needs a callback`);let t=t=>e(t);return l.add(t),l.size===1?h():u&&queueMicrotask(()=>l.has(t)&&e(u)),()=>{l.delete(t)&&l.size===0&&h()}}});Object.defineProperty(window,\"camox\",{value:_,enumerable:!0}),e!==window&&e.postMessage({camox:`hello`},`*`)})();";
|
|
34
36
|
|
|
35
37
|
//#endregion
|
|
36
38
|
export { ARTIFACT_RUNTIME };
|
|
@@ -39,7 +39,7 @@ function CanvasPageHeader({ page, pathname, selected, onSelect, onHoverChange, o
|
|
|
39
39
|
}
|
|
40
40
|
};
|
|
41
41
|
const path = pathname ?? (page.kind === "derived" ? page.pattern : void 0);
|
|
42
|
-
const pathClassName = "text-muted-foreground w-full min-w-0 truncate text-left text-xs font-normal";
|
|
42
|
+
const pathClassName = "text-muted-foreground max-w-full min-w-0 truncate text-left text-xs font-normal";
|
|
43
43
|
const focusClassName = "focus-visible:ring-ring outline-none focus-visible:ring-2";
|
|
44
44
|
const name = draft !== null ? /* @__PURE__ */ jsx("input", {
|
|
45
45
|
"aria-label": "Page nickname",
|
|
@@ -4,6 +4,16 @@ import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "../preview/components/previ
|
|
|
4
4
|
//#region src/features/canvas/canvasFrameDocument.ts
|
|
5
5
|
/** Freezes CSS animations, including ones that start while the frame is paused. */
|
|
6
6
|
const PAUSED_ANIMATIONS_STYLE = `html[${PREVIEW_PAUSED_ATTRIBUTE}] *, html[${PREVIEW_PAUSED_ATTRIBUTE}] *::before, html[${PREVIEW_PAUSED_ATTRIBUTE}] *::after { animation-play-state: paused !important; }`;
|
|
7
|
+
/**
|
|
8
|
+
* A style of the studio's own for a frame's document, marked so that giving the frame a site
|
|
9
|
+
* document's stylesheets (`restyle`) leaves it.
|
|
10
|
+
*/
|
|
11
|
+
function studioStyle(doc, css) {
|
|
12
|
+
const style = doc.createElement("style");
|
|
13
|
+
style.setAttribute("data-camox-studio", "");
|
|
14
|
+
style.textContent = css;
|
|
15
|
+
return style;
|
|
16
|
+
}
|
|
7
17
|
/** Set by the camera on elements that hold Canvas frames: see `canvasBlockOverlap`. */
|
|
8
18
|
const BLOCK_OVERLAP_ATTRIBUTE = "data-canvas-block-overlap";
|
|
9
19
|
const BLOCK = "[data-camox-viewport-block]";
|
|
@@ -93,8 +103,7 @@ function createCanvasDocument(previewDocument, href) {
|
|
|
93
103
|
doc.querySelector("[data-camox-preview-root]")?.replaceChildren();
|
|
94
104
|
doc.documentElement.style.setProperty("overscroll-behavior", "none");
|
|
95
105
|
doc.body.style.setProperty("overscroll-behavior", "none");
|
|
96
|
-
const paused = doc
|
|
97
|
-
paused.textContent = PAUSED_ANIMATIONS_STYLE;
|
|
106
|
+
const paused = studioStyle(doc, PAUSED_ANIMATIONS_STYLE);
|
|
98
107
|
doc.head.appendChild(paused);
|
|
99
108
|
return `<!doctype html>${doc.documentElement.outerHTML}`;
|
|
100
109
|
}
|
|
@@ -106,8 +115,7 @@ function createCanvasDocument(previewDocument, href) {
|
|
|
106
115
|
*/
|
|
107
116
|
function adoptCanvasDocument(iframe, root, viewportHeight) {
|
|
108
117
|
const doc = root.ownerDocument;
|
|
109
|
-
const paused = doc
|
|
110
|
-
paused.textContent = PAUSED_ANIMATIONS_STYLE;
|
|
118
|
+
const paused = studioStyle(doc, PAUSED_ANIMATIONS_STYLE);
|
|
111
119
|
doc.head.appendChild(paused);
|
|
112
120
|
const restored = [doc.documentElement, doc.body].map((element) => ({
|
|
113
121
|
element,
|
|
@@ -199,8 +207,7 @@ function observeCanvasDocument(iframe, root, viewportHeight, suspended = () => f
|
|
|
199
207
|
doc.documentElement.style.setProperty("--camox-viewport-height", `${baseline}px`);
|
|
200
208
|
doc.documentElement.style.setProperty("overflow", "hidden", "important");
|
|
201
209
|
doc.body.style.setProperty("overflow", "hidden", "important");
|
|
202
|
-
const overlapStyle = doc
|
|
203
|
-
overlapStyle.textContent = BLOCK_OVERLAP_STYLE;
|
|
210
|
+
const overlapStyle = studioStyle(doc, BLOCK_OVERLAP_STYLE);
|
|
204
211
|
doc.head.appendChild(overlapStyle);
|
|
205
212
|
const followOverlap = () => {
|
|
206
213
|
const overlap = iframe.closest(`[${BLOCK_OVERLAP_ATTRIBUTE}]`);
|
|
@@ -5,7 +5,7 @@ import { PageViewportContext } from "../../runtime/pageViewport.js";
|
|
|
5
5
|
import { PreviewDocumentContext } from "../../runtime/PreviewDocumentContext.js";
|
|
6
6
|
import { ViewportContinuityContext, capturePreviewAnchor, restorePreviewAnchor } from "../viewportContinuity.js";
|
|
7
7
|
import { PreviewActivationContext } from "./PreviewActivation.js";
|
|
8
|
-
import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle } from "./previewStyles.js";
|
|
8
|
+
import { EMPTY_PREVIEW_DOCUMENT, isSiteStyle, restyle } from "./previewStyles.js";
|
|
9
9
|
import { c } from "react/compiler-runtime";
|
|
10
10
|
import * as React from "react";
|
|
11
11
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -21,23 +21,6 @@ function useFrame() {
|
|
|
21
21
|
if (!context) throw new Error("useFrame must be used within a Frame");
|
|
22
22
|
return context;
|
|
23
23
|
}
|
|
24
|
-
/** The stylesheets of a document's head that are the site's: not the studio's. */
|
|
25
|
-
const SITE_STYLESHEETS = "link[rel~=\"stylesheet\"], style";
|
|
26
|
-
const siteStylesheetsOf = (head) => Array.from(head.querySelectorAll(SITE_STYLESHEETS)).filter((element) => element.parentElement === head && isSiteStyle(element));
|
|
27
|
-
/**
|
|
28
|
-
* Gives the frame's document the site stylesheets of `html`, a site document: the ones it no
|
|
29
|
-
* longer has go, and its new ones are added, the rest of the frame left as it is.
|
|
30
|
-
*/
|
|
31
|
-
function restyle(frame, html) {
|
|
32
|
-
const doc = frame.document;
|
|
33
|
-
const { DOMParser } = frame;
|
|
34
|
-
const wanted = siteStylesheetsOf(new DOMParser().parseFromString(html, "text/html").head);
|
|
35
|
-
const held = siteStylesheetsOf(doc.head);
|
|
36
|
-
const wantedMarkup = new Set(wanted.map((element) => element.outerHTML));
|
|
37
|
-
const heldMarkup = new Set(held.map((element) => element.outerHTML));
|
|
38
|
-
for (const element of held) if (!wantedMarkup.has(element.outerHTML)) element.remove();
|
|
39
|
-
for (const element of wanted) if (!heldMarkup.has(element.outerHTML)) doc.head.appendChild(doc.importNode(element, true));
|
|
40
|
-
}
|
|
41
24
|
function ClearServerMarkup(t0) {
|
|
42
25
|
const $ = c(4);
|
|
43
26
|
const { nodes } = t0;
|