webstudio 0.268.0 → 0.270.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "webstudio",
3
- "version": "0.268.0",
3
+ "version": "0.270.0",
4
4
  "description": "Webstudio CLI",
5
5
  "author": "Webstudio <github@webstudio.is>",
6
6
  "homepage": "https://webstudio.is",
@@ -43,8 +43,8 @@
43
43
  "warn-once": "^0.1.1",
44
44
  "yargs": "^17.7.2",
45
45
  "zod": "^3.24.2",
46
- "@webstudio-is/trpc-interface": "0.268.0",
47
- "@webstudio-is/project-migrations": "0.268.0"
46
+ "@webstudio-is/project-migrations": "0.270.0",
47
+ "@webstudio-is/trpc-interface": "0.270.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@cloudflare/vite-plugin": "^1.1.0",
@@ -74,18 +74,18 @@
74
74
  "vite": "^6.3.4",
75
75
  "vitest": "^3.1.2",
76
76
  "wrangler": "^3.63.2",
77
- "@webstudio-is/css-engine": "0.268.0",
78
- "@webstudio-is/http-client": "0.268.0",
79
- "@webstudio-is/image": "0.268.0",
80
- "@webstudio-is/react-sdk": "0.268.0",
81
- "@webstudio-is/sdk": "0.268.0",
82
- "@webstudio-is/sdk-components-animation": "0.268.0",
83
- "@webstudio-is/sdk-components-react-radix": "0.268.0",
84
- "@webstudio-is/sdk-components-react-remix": "0.268.0",
85
- "@webstudio-is/sdk-components-react-router": "0.268.0",
86
- "@webstudio-is/sdk-components-react": "0.268.0",
87
- "@webstudio-is/wsauth": "0.268.0",
88
- "@webstudio-is/tsconfig": "1.0.7"
77
+ "@webstudio-is/css-engine": "0.270.0",
78
+ "@webstudio-is/image": "0.270.0",
79
+ "@webstudio-is/http-client": "0.270.0",
80
+ "@webstudio-is/react-sdk": "0.270.0",
81
+ "@webstudio-is/sdk": "0.270.0",
82
+ "@webstudio-is/sdk-components-animation": "0.270.0",
83
+ "@webstudio-is/sdk-components-react": "0.270.0",
84
+ "@webstudio-is/sdk-components-react-radix": "0.270.0",
85
+ "@webstudio-is/sdk-components-react-remix": "0.270.0",
86
+ "@webstudio-is/tsconfig": "1.0.7",
87
+ "@webstudio-is/sdk-components-react-router": "0.270.0",
88
+ "@webstudio-is/wsauth": "0.270.0"
89
89
  },
90
90
  "scripts": {
91
91
  "typecheck": "tsgo --noEmit",
@@ -12,7 +12,7 @@
12
12
  "build-cf-types": "wrangler types"
13
13
  },
14
14
  "dependencies": {
15
- "@webstudio-is/wsauth": "0.268.0",
15
+ "@webstudio-is/wsauth": "0.270.0",
16
16
  "@remix-run/cloudflare": "2.16.5",
17
17
  "@remix-run/cloudflare-pages": "2.16.5"
18
18
  },
@@ -0,0 +1,167 @@
1
+ import {
2
+ createPath,
3
+ generatePath,
4
+ matchPath,
5
+ parsePath,
6
+ } from "@remix-run/react";
7
+ import { redirect } from "@remix-run/server-runtime";
8
+
9
+ type RedirectItem = {
10
+ old: string;
11
+ new: string;
12
+ status?: number | string;
13
+ };
14
+
15
+ type MatchedRedirect = {
16
+ url: string;
17
+ status: number;
18
+ };
19
+
20
+ /**
21
+ * Expands route params in local redirect targets.
22
+ * External and protocol-relative URLs are returned unchanged because route params
23
+ * only apply to app paths.
24
+ */
25
+ export const generateRedirectUrl = (
26
+ url: string,
27
+ params: Record<string, string | undefined>
28
+ ) => {
29
+ if (
30
+ /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(url) ||
31
+ url.startsWith("//") ||
32
+ url.startsWith("?") ||
33
+ url.startsWith("#")
34
+ ) {
35
+ return url;
36
+ }
37
+
38
+ const targetParams = Object.fromEntries(
39
+ Object.entries(params).map(([name, value]) => [
40
+ name,
41
+ name === "*" ? value : value?.replaceAll("/", "%2F"),
42
+ ])
43
+ );
44
+ const path = parsePath(url);
45
+ try {
46
+ return createPath({
47
+ ...path,
48
+ pathname: generatePath(path.pathname ?? "/", targetParams),
49
+ });
50
+ } catch {
51
+ return url;
52
+ }
53
+ };
54
+
55
+ const stripHash = (source: string) => {
56
+ const hashIndex = source.indexOf("#");
57
+ return hashIndex === -1 ? source : source.slice(0, hashIndex);
58
+ };
59
+
60
+ const getRedirectStatus = (status: RedirectItem["status"]) => {
61
+ return Number(status) === 302 ? 302 : 301;
62
+ };
63
+
64
+ const decodePathname = (pathname: string) => {
65
+ try {
66
+ return decodeURI(pathname);
67
+ } catch {
68
+ return pathname;
69
+ }
70
+ };
71
+
72
+ const getPathnameVariants = (pathname: string) => {
73
+ return Array.from(new Set([pathname, decodePathname(pathname)]));
74
+ };
75
+
76
+ const isOptionalSegmentMarker = (source: string, index: number) => {
77
+ const nextChar = source[index + 1];
78
+ if (nextChar !== undefined && nextChar !== "/") {
79
+ return false;
80
+ }
81
+
82
+ const segmentStart = source.lastIndexOf("/", index - 1);
83
+ const segment = source.slice(segmentStart + 1, index);
84
+ return segment !== "";
85
+ };
86
+
87
+ const getSourceSearchIndex = (source: string) => {
88
+ for (let index = 0; index < source.length; index += 1) {
89
+ if (
90
+ source[index] === "?" &&
91
+ isOptionalSegmentMarker(source, index) === false
92
+ ) {
93
+ return index;
94
+ }
95
+ }
96
+ return -1;
97
+ };
98
+
99
+ const isRedirectPattern = (source: string) => {
100
+ return (
101
+ /(^|\/):[^/]+/.test(source) ||
102
+ /(^|\/)\*(?=\/|$)/.test(source) ||
103
+ /(^|\/)[^/]+\?(?=\/|$)/.test(source)
104
+ );
105
+ };
106
+
107
+ export const matchRedirect = (
108
+ requestUrl: string,
109
+ redirects: RedirectItem[]
110
+ ): MatchedRedirect | undefined => {
111
+ const url = new URL(requestUrl);
112
+ const requestPathnames = getPathnameVariants(url.pathname);
113
+
114
+ for (const redirect of redirects) {
115
+ const source = stripHash(redirect.old);
116
+ if (getSourceSearchIndex(source) !== -1) {
117
+ const exactMatch = requestPathnames.some(
118
+ (requestPathname) => source === `${requestPathname}${url.search}`
119
+ );
120
+ if (exactMatch) {
121
+ return {
122
+ url: generateRedirectUrl(redirect.new, {}),
123
+ status: getRedirectStatus(redirect.status),
124
+ };
125
+ }
126
+ continue;
127
+ }
128
+
129
+ if (isRedirectPattern(source) === false) {
130
+ if (requestPathnames.includes(source)) {
131
+ return {
132
+ url: generateRedirectUrl(redirect.new, {}),
133
+ status: getRedirectStatus(redirect.status),
134
+ };
135
+ }
136
+ continue;
137
+ }
138
+
139
+ const match = requestPathnames
140
+ .map((requestPathname) =>
141
+ matchPath(
142
+ { path: source, caseSensitive: true, end: true },
143
+ requestPathname
144
+ )
145
+ )
146
+ .find((match) => match !== null);
147
+ if (match === undefined) {
148
+ continue;
149
+ }
150
+
151
+ return {
152
+ url: generateRedirectUrl(redirect.new, match.params),
153
+ status: getRedirectStatus(redirect.status),
154
+ };
155
+ }
156
+ };
157
+
158
+ export const redirectRequest = (
159
+ request: Request,
160
+ redirects: RedirectItem[]
161
+ ): Response | undefined => {
162
+ const matchedRedirect = matchRedirect(request.url, redirects);
163
+ if (matchedRedirect === undefined) {
164
+ return;
165
+ }
166
+ return redirect(matchedRedirect.url, matchedRedirect.status);
167
+ };
@@ -1,10 +1,25 @@
1
1
  /* eslint-disable @typescript-eslint/ban-ts-comment */
2
2
 
3
3
  import { Links, Meta, Outlet, useMatches } from "@remix-run/react";
4
- import type { HeadersFunction } from "@remix-run/server-runtime";
4
+ import type {
5
+ HeadersFunction,
6
+ LoaderFunctionArgs,
7
+ } from "@remix-run/server-runtime";
8
+ import { redirectRequest } from "./redirect-url";
5
9
  // @todo think about how to make __generated__ typeable
6
10
  // @ts-ignore
7
11
  import { CustomCode, projectId, lastPublished } from "./__generated__/_index";
12
+ // @ts-ignore
13
+ import { redirects } from "./__generated__/$resources.redirects";
14
+
15
+ export const loader = ({ request }: LoaderFunctionArgs) => {
16
+ const redirectResponse = redirectRequest(request, redirects);
17
+ if (redirectResponse !== undefined) {
18
+ return redirectResponse;
19
+ }
20
+
21
+ return null;
22
+ };
8
23
 
9
24
  export const headers: HeadersFunction = ({ errorHeaders }) => {
10
25
  if (errorHeaders) {
@@ -1,3 +1,4 @@
1
+ import { type ComponentProps, memo, useMemo } from "react";
1
2
  import {
2
3
  type ServerRuntimeMetaFunction as MetaFunction,
3
4
  type LinksFunction,
@@ -328,20 +329,33 @@ export const action = async ({
328
329
  }
329
330
  };
330
331
 
332
+ const PageBoundary = memo(
333
+ ({ url, system }: ComponentProps<typeof Page> & { url: string }) => {
334
+ // Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages
335
+ return <Page key={url} system={system} />;
336
+ },
337
+ // React Router can rerender the current route while the next route loaders are
338
+ // still pending. Keep the generated page out of that pending-navigation render
339
+ // path, but let URL changes remount it.
340
+ (prevProps, nextProps) => prevProps.url === nextProps.url
341
+ );
342
+
331
343
  const Outlet = () => {
332
344
  const { system, resources, url, pageMeta, host } =
333
345
  useLoaderData<typeof loader>();
346
+ const sdkContext = useMemo(
347
+ () => ({
348
+ ...constants,
349
+ resources,
350
+ breakpoints,
351
+ onError: console.error,
352
+ }),
353
+ [resources]
354
+ );
355
+
334
356
  return (
335
- <ReactSdkContext.Provider
336
- value={{
337
- ...constants,
338
- resources,
339
- breakpoints,
340
- onError: console.error,
341
- }}
342
- >
343
- {/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
344
- <Page key={url} system={system} />
357
+ <ReactSdkContext.Provider value={sdkContext}>
358
+ <PageBoundary url={url} system={system} />
345
359
  <PageSettingsMeta
346
360
  url={url}
347
361
  pageMeta={pageMeta}
@@ -11,14 +11,14 @@
11
11
  "@remix-run/node": "2.16.5",
12
12
  "@remix-run/react": "2.16.5",
13
13
  "@remix-run/server-runtime": "2.16.5",
14
- "@webstudio-is/image": "0.268.0",
15
- "@webstudio-is/react-sdk": "0.268.0",
16
- "@webstudio-is/sdk": "0.268.0",
17
- "@webstudio-is/sdk-components-react": "0.268.0",
18
- "@webstudio-is/sdk-components-animation": "0.268.0",
19
- "@webstudio-is/sdk-components-react-radix": "0.268.0",
20
- "@webstudio-is/sdk-components-react-remix": "0.268.0",
21
- "@webstudio-is/wsauth": "0.268.0",
14
+ "@webstudio-is/image": "0.270.0",
15
+ "@webstudio-is/react-sdk": "0.270.0",
16
+ "@webstudio-is/sdk": "0.270.0",
17
+ "@webstudio-is/sdk-components-react": "0.270.0",
18
+ "@webstudio-is/sdk-components-animation": "0.270.0",
19
+ "@webstudio-is/sdk-components-react-radix": "0.270.0",
20
+ "@webstudio-is/sdk-components-react-remix": "0.270.0",
21
+ "@webstudio-is/wsauth": "0.270.0",
22
22
  "isbot": "^5.1.25",
23
23
  "react": "18.3.0-canary-14898b6a9-20240318",
24
24
  "react-dom": "18.3.0-canary-14898b6a9-20240318"
@@ -0,0 +1,167 @@
1
+ import {
2
+ createPath,
3
+ generatePath,
4
+ matchPath,
5
+ parsePath,
6
+ redirect,
7
+ } from "react-router";
8
+
9
+ type RedirectItem = {
10
+ old: string;
11
+ new: string;
12
+ status?: number | string;
13
+ };
14
+
15
+ type MatchedRedirect = {
16
+ url: string;
17
+ status: number;
18
+ };
19
+
20
+ /**
21
+ * Expands route params in local redirect targets.
22
+ * External and protocol-relative URLs are returned unchanged because route params
23
+ * only apply to app paths.
24
+ */
25
+ export const generateRedirectUrl = (
26
+ url: string,
27
+ params: Record<string, string | undefined>
28
+ ) => {
29
+ if (
30
+ /^[a-zA-Z][a-zA-Z\d+.-]*:/.test(url) ||
31
+ url.startsWith("//") ||
32
+ url.startsWith("?") ||
33
+ url.startsWith("#")
34
+ ) {
35
+ return url;
36
+ }
37
+
38
+ const targetParams = Object.fromEntries(
39
+ Object.entries(params).map(([name, value]) => [
40
+ name,
41
+ name === "*" ? value : value?.replaceAll("/", "%2F"),
42
+ ])
43
+ );
44
+ const path = parsePath(url);
45
+ try {
46
+ return createPath({
47
+ ...path,
48
+ pathname: generatePath(path.pathname ?? "/", targetParams),
49
+ });
50
+ } catch {
51
+ return url;
52
+ }
53
+ };
54
+
55
+ const stripHash = (source: string) => {
56
+ const hashIndex = source.indexOf("#");
57
+ return hashIndex === -1 ? source : source.slice(0, hashIndex);
58
+ };
59
+
60
+ const getRedirectStatus = (status: RedirectItem["status"]) => {
61
+ return Number(status) === 302 ? 302 : 301;
62
+ };
63
+
64
+ const decodePathname = (pathname: string) => {
65
+ try {
66
+ return decodeURI(pathname);
67
+ } catch {
68
+ return pathname;
69
+ }
70
+ };
71
+
72
+ const getPathnameVariants = (pathname: string) => {
73
+ return Array.from(new Set([pathname, decodePathname(pathname)]));
74
+ };
75
+
76
+ const isOptionalSegmentMarker = (source: string, index: number) => {
77
+ const nextChar = source[index + 1];
78
+ if (nextChar !== undefined && nextChar !== "/") {
79
+ return false;
80
+ }
81
+
82
+ const segmentStart = source.lastIndexOf("/", index - 1);
83
+ const segment = source.slice(segmentStart + 1, index);
84
+ return segment !== "";
85
+ };
86
+
87
+ const getSourceSearchIndex = (source: string) => {
88
+ for (let index = 0; index < source.length; index += 1) {
89
+ if (
90
+ source[index] === "?" &&
91
+ isOptionalSegmentMarker(source, index) === false
92
+ ) {
93
+ return index;
94
+ }
95
+ }
96
+ return -1;
97
+ };
98
+
99
+ const isRedirectPattern = (source: string) => {
100
+ return (
101
+ /(^|\/):[^/]+/.test(source) ||
102
+ /(^|\/)\*(?=\/|$)/.test(source) ||
103
+ /(^|\/)[^/]+\?(?=\/|$)/.test(source)
104
+ );
105
+ };
106
+
107
+ export const matchRedirect = (
108
+ requestUrl: string,
109
+ redirects: RedirectItem[]
110
+ ): MatchedRedirect | undefined => {
111
+ const url = new URL(requestUrl);
112
+ const requestPathnames = getPathnameVariants(url.pathname);
113
+
114
+ for (const redirect of redirects) {
115
+ const source = stripHash(redirect.old);
116
+ if (getSourceSearchIndex(source) !== -1) {
117
+ const exactMatch = requestPathnames.some(
118
+ (requestPathname) => source === `${requestPathname}${url.search}`
119
+ );
120
+ if (exactMatch) {
121
+ return {
122
+ url: generateRedirectUrl(redirect.new, {}),
123
+ status: getRedirectStatus(redirect.status),
124
+ };
125
+ }
126
+ continue;
127
+ }
128
+
129
+ if (isRedirectPattern(source) === false) {
130
+ if (requestPathnames.includes(source)) {
131
+ return {
132
+ url: generateRedirectUrl(redirect.new, {}),
133
+ status: getRedirectStatus(redirect.status),
134
+ };
135
+ }
136
+ continue;
137
+ }
138
+
139
+ const match = requestPathnames
140
+ .map((requestPathname) =>
141
+ matchPath(
142
+ { path: source, caseSensitive: true, end: true },
143
+ requestPathname
144
+ )
145
+ )
146
+ .find((match) => match !== null);
147
+ if (match === undefined) {
148
+ continue;
149
+ }
150
+
151
+ return {
152
+ url: generateRedirectUrl(redirect.new, match.params),
153
+ status: getRedirectStatus(redirect.status),
154
+ };
155
+ }
156
+ };
157
+
158
+ export const redirectRequest = (
159
+ request: Request,
160
+ redirects: RedirectItem[]
161
+ ): Response | undefined => {
162
+ const matchedRedirect = matchRedirect(request.url, redirects);
163
+ if (matchedRedirect === undefined) {
164
+ return;
165
+ }
166
+ return redirect(matchedRedirect.url, matchedRedirect.status);
167
+ };
@@ -2,14 +2,27 @@
2
2
 
3
3
  import {
4
4
  type HeadersFunction,
5
+ type LoaderFunctionArgs,
5
6
  Links,
6
7
  Meta,
7
8
  Outlet,
8
9
  useMatches,
9
10
  } from "react-router";
11
+ import { redirectRequest } from "./redirect-url";
10
12
  // @todo think about how to make __generated__ typeable
11
13
  // @ts-ignore
12
14
  import { CustomCode, projectId, lastPublished } from "./__generated__/_index";
15
+ // @ts-ignore
16
+ import { redirects } from "./__generated__/$resources.redirects";
17
+
18
+ export const loader = ({ request }: LoaderFunctionArgs) => {
19
+ const redirectResponse = redirectRequest(request, redirects);
20
+ if (redirectResponse !== undefined) {
21
+ return redirectResponse;
22
+ }
23
+
24
+ return null;
25
+ };
13
26
 
14
27
  export const headers: HeadersFunction = ({ errorHeaders }) => {
15
28
  if (errorHeaders) {
@@ -1,3 +1,4 @@
1
+ import { type ComponentProps, memo, useMemo } from "react";
1
2
  import {
2
3
  type MetaFunction,
3
4
  type LinksFunction,
@@ -327,20 +328,33 @@ export const action = async ({
327
328
  }
328
329
  };
329
330
 
331
+ const PageBoundary = memo(
332
+ ({ url, system }: ComponentProps<typeof Page> & { url: string }) => {
333
+ // Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages
334
+ return <Page key={url} system={system} />;
335
+ },
336
+ // React Router can rerender the current route while the next route loaders are
337
+ // still pending. Keep the generated page out of that pending-navigation render
338
+ // path, but let URL changes remount it.
339
+ (prevProps, nextProps) => prevProps.url === nextProps.url
340
+ );
341
+
330
342
  const Outlet = () => {
331
343
  const { system, resources, url, pageMeta, host } =
332
344
  useLoaderData<typeof loader>();
345
+ const sdkContext = useMemo(
346
+ () => ({
347
+ ...constants,
348
+ resources,
349
+ breakpoints,
350
+ onError: console.error,
351
+ }),
352
+ [resources]
353
+ );
354
+
333
355
  return (
334
- <ReactSdkContext.Provider
335
- value={{
336
- ...constants,
337
- resources,
338
- breakpoints,
339
- onError: console.error,
340
- }}
341
- >
342
- {/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
343
- <Page key={url} system={system} />
356
+ <ReactSdkContext.Provider value={sdkContext}>
357
+ <PageBoundary url={url} system={system} />
344
358
  <PageSettingsMeta
345
359
  url={url}
346
360
  pageMeta={pageMeta}
@@ -10,14 +10,14 @@
10
10
  "dependencies": {
11
11
  "@react-router/dev": "^7.5.3",
12
12
  "@react-router/fs-routes": "^7.5.3",
13
- "@webstudio-is/image": "0.268.0",
14
- "@webstudio-is/react-sdk": "0.268.0",
15
- "@webstudio-is/sdk": "0.268.0",
16
- "@webstudio-is/sdk-components-animation": "0.268.0",
17
- "@webstudio-is/sdk-components-react-radix": "0.268.0",
18
- "@webstudio-is/sdk-components-react-router": "0.268.0",
19
- "@webstudio-is/sdk-components-react": "0.268.0",
20
- "@webstudio-is/wsauth": "0.268.0",
13
+ "@webstudio-is/image": "0.270.0",
14
+ "@webstudio-is/react-sdk": "0.270.0",
15
+ "@webstudio-is/sdk": "0.270.0",
16
+ "@webstudio-is/sdk-components-animation": "0.270.0",
17
+ "@webstudio-is/sdk-components-react-radix": "0.270.0",
18
+ "@webstudio-is/sdk-components-react-router": "0.270.0",
19
+ "@webstudio-is/sdk-components-react": "0.270.0",
20
+ "@webstudio-is/wsauth": "0.270.0",
21
21
  "isbot": "^5.1.25",
22
22
  "react": "18.3.0-canary-14898b6a9-20240318",
23
23
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
@@ -7,7 +7,7 @@
7
7
  },
8
8
  "dependencies": {
9
9
  "@cloudflare/vite-plugin": "^1.1.0",
10
- "@webstudio-is/wsauth": "0.268.0",
10
+ "@webstudio-is/wsauth": "0.270.0",
11
11
  "wrangler": "^4.14.1"
12
12
  }
13
13
  }
@@ -1,26 +1,49 @@
1
+ import { type ComponentProps, memo, useMemo } from "react";
1
2
  import type { PageContext } from "vike/types";
2
3
  import {
3
4
  PageSettingsMeta,
4
5
  PageSettingsTitle,
5
6
  ReactSdkContext,
6
7
  } from "@webstudio-is/react-sdk/runtime";
8
+ import { LinkCurrentUrlContext } from "@webstudio-is/sdk-components-react";
7
9
  import { assetBaseUrl, imageLoader } from "__CONSTANTS__";
8
10
  import { Page, breakpoints, siteName } from "__CLIENT__";
9
11
 
12
+ const getPageKey = (url: string) => {
13
+ const { origin, pathname, search } = new URL(url);
14
+ return `${origin}${pathname}${search}`;
15
+ };
16
+
17
+ const PageBoundary = memo(
18
+ ({ pageKey, system }: ComponentProps<typeof Page> & { pageKey: string }) => {
19
+ // Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages
20
+ return <Page key={pageKey} system={system} />;
21
+ },
22
+ // Vike can rerender the current page during client-side navigation and
23
+ // hash-only URL updates. Keep the generated page out of that render path,
24
+ // but let actual page URL changes remount it.
25
+ (prevProps, nextProps) => prevProps.pageKey === nextProps.pageKey
26
+ );
27
+
10
28
  const PageComponent = ({ data }: { data: PageContext["data"] }) => {
11
29
  const { system, resources, url, pageMeta } = data;
30
+ const pageKey = getPageKey(url);
31
+ const sdkContext = useMemo(
32
+ () => ({
33
+ imageLoader,
34
+ assetBaseUrl,
35
+ resources,
36
+ breakpoints,
37
+ onError: console.error,
38
+ }),
39
+ [resources]
40
+ );
41
+
12
42
  return (
13
- <ReactSdkContext.Provider
14
- value={{
15
- imageLoader,
16
- assetBaseUrl,
17
- resources,
18
- breakpoints,
19
- onError: console.error,
20
- }}
21
- >
22
- {/* Use the URL as the key to force scripts in HTML Embed to reload on dynamic pages */}
23
- <Page key={url} system={system} />
43
+ <ReactSdkContext.Provider value={sdkContext}>
44
+ <LinkCurrentUrlContext.Provider value={url}>
45
+ <PageBoundary pageKey={pageKey} system={system} />
46
+ </LinkCurrentUrlContext.Provider>
24
47
  <PageSettingsMeta
25
48
  url={url}
26
49
  pageMeta={pageMeta}
@@ -8,12 +8,12 @@
8
8
  "typecheck": "tsgo --noEmit"
9
9
  },
10
10
  "dependencies": {
11
- "@webstudio-is/image": "0.268.0",
12
- "@webstudio-is/react-sdk": "0.268.0",
13
- "@webstudio-is/sdk": "0.268.0",
14
- "@webstudio-is/sdk-components-react": "0.268.0",
15
- "@webstudio-is/sdk-components-animation": "0.268.0",
16
- "@webstudio-is/sdk-components-react-radix": "0.268.0",
11
+ "@webstudio-is/image": "0.270.0",
12
+ "@webstudio-is/react-sdk": "0.270.0",
13
+ "@webstudio-is/sdk": "0.270.0",
14
+ "@webstudio-is/sdk-components-react": "0.270.0",
15
+ "@webstudio-is/sdk-components-animation": "0.270.0",
16
+ "@webstudio-is/sdk-components-react-radix": "0.270.0",
17
17
  "react": "18.3.0-canary-14898b6a9-20240318",
18
18
  "react-dom": "18.3.0-canary-14898b6a9-20240318",
19
19
  "vike": "^0.4.229"