UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

869 lines (868 loc) 36.8 kB
import { jsxs, jsx } from "react/jsx-runtime"; import { c } from "react-compiler-runtime"; import { useState, useEffect, memo, Fragment } from "react"; import { isRecord, useDocumentStore, useTranslation, usePerspective, useEditState, useSchema, unstable_useValuePreview, _isCustomDocumentTypeDefinition, useWorkspace, SourceProvider } from "sanity"; import { generateHelpUrl, Button, ErrorBoundary } from "./generate-help-url.esm.mjs"; import { useStructureTool, LOADING_PANE, useDocumentLastRev, PaneLayout, LoadingPane, StructureToolPane, setActivePanes } from "./pane.mjs"; import { structureLocaleNamespace, SerializeError, StructureToolProvider } from "./StructureToolProvider.mjs"; import { useRouter, useRouterState } from "sanity/router"; import omit from "lodash/omit.js"; import { from, of, isObservable, firstValueFrom, NEVER, concat, ReplaySubject } from "rxjs"; import { nanoid } from "nanoid"; import { switchMap, publishReplay, refCount, map, startWith, pairwise, scan, distinctUntilChanged } from "rxjs/operators"; import { uuid } from "@sanity/uuid"; import { SyncIcon, WarningOutlineIcon } from "@sanity/icons"; import { Stack, Text, Code, Box, Card, Container, Heading, Flex, useToast, useTheme, PortalProvider } from "@sanity/ui"; import { styled } from "styled-components"; import "../_singletons.mjs"; import { isHotkey } from "is-hotkey-esm"; import isEqual from "lodash/isEqual.js"; class PaneResolutionError extends Error { constructor({ message, context, helpId, cause }) { super(message), this.name = "PaneResolutionError", this.context = context, this.helpId = helpId, this.cause = cause; } } const randomIdCache = /* @__PURE__ */ new WeakMap(); function assignId(obj) { const cachedValue = randomIdCache.get(obj); if (cachedValue) return cachedValue; const id = nanoid(); return randomIdCache.set(obj, id), id; } const isPromise = (thing) => !!thing && typeof thing?.then == "function", isSerializable = (thing) => isRecord(thing) ? typeof thing.serialize == "function" : !1, rethrowWithPaneResolutionErrors = (next) => (unresolvedPane, context, flatIndex) => { try { return next(unresolvedPane, context, flatIndex); } catch (e) { throw e instanceof PaneResolutionError ? e : new PaneResolutionError({ message: typeof e?.message == "string" ? e.message : "", context, cause: e }); } }, wrapWithPublishReplay = (next) => (...args) => next(...args).pipe( // need to add publishReplay + refCount to ensure new subscribers always // get an emission. without this, memoized observables may get stuck // waiting for their first emissions resulting in a loading pane publishReplay(1), refCount() ); function createPaneResolver(middleware) { const resolvePane = rethrowWithPaneResolutionErrors(wrapWithPublishReplay(middleware((unresolvedPane, context, flatIndex) => { if (!unresolvedPane) throw new PaneResolutionError({ message: "Pane returned no child", context, helpId: "structure-item-returned-no-child" }); return isPromise(unresolvedPane) || isObservable(unresolvedPane) ? from(unresolvedPane).pipe(switchMap((result) => resolvePane(result, context, flatIndex))) : isSerializable(unresolvedPane) ? resolvePane(unresolvedPane.serialize(context), context, flatIndex) : typeof unresolvedPane == "function" ? resolvePane(unresolvedPane(context.id, context), context, flatIndex) : of(unresolvedPane); }))); return resolvePane; } const bindCache = /* @__PURE__ */ new WeakMap(); function memoBind(obj, methodKey) { const boundMethods = bindCache.get(obj) || /* @__PURE__ */ new Map(); if (boundMethods) { const bound2 = boundMethods.get(methodKey); if (bound2) return bound2; } const method = obj[methodKey]; if (typeof method != "function") throw new Error(`Expected property \`${methodKey}\` to be a function but got ${typeof method} instead.`); const bound = method.bind(obj); return boundMethods.set(methodKey, bound), bindCache.set(obj, boundMethods), bound; } async function resolveIntent(options) { const resolvedPaneCache = /* @__PURE__ */ new Map(), resolvePane = createPaneResolver((nextFn) => (unresolvedPane, context, flatIndex) => { const key = unresolvedPane && `${assignId(unresolvedPane)}-${context.path.join("__")}`, cachedResolvedPane = key && resolvedPaneCache.get(key); if (cachedResolvedPane) return cachedResolvedPane; const result = nextFn(unresolvedPane, context, flatIndex); return key && resolvedPaneCache.set(key, result), result; }), fallbackEditorPanes = [[{ id: `__edit__${options.params.id}`, params: { ...omit(options.params, ["id"]), type: options.params.type }, payload: options.payload }]]; async function traverse({ currentId, flatIndex, intent, params, parent: parent2, path, payload, unresolvedPane, levelIndex, structureContext }) { if (!unresolvedPane) return []; const { id: targetId, type: schemaTypeName, ...otherParams } = params, resolvedPane = await firstValueFrom(resolvePane(unresolvedPane, { id: currentId, splitIndex: 0, parent: parent2, path, index: flatIndex, params: {}, payload: void 0, structureContext }, flatIndex)); return resolvedPane.type === "document" && resolvedPane.id === targetId ? [{ panes: [...path.slice(0, path.length - 1).map((i) => [{ id: i }]), [{ id: targetId, params: otherParams, payload }]], depthIndex: path.length, levelIndex }] : ( // if the resolve pane's `canHandleIntent` returns true, then resolve resolvedPane.canHandleIntent?.(intent, params, { pane: resolvedPane, index: flatIndex }) || // if the pane's `canHandleIntent` did not return true, then match against // this default case. we will resolve the intent if: resolvedPane.type === "documentList" && // 1. the schema type matches (this required for the document to render) resolvedPane.schemaTypeName === schemaTypeName && // 2. the filter is the default filter. // // NOTE: this case is to prevent false positive matches where the user // has configured a more specific filter for a particular type. In that // case, the user can implement their own `canHandleIntent` function resolvedPane.options.filter === "_type == $type" ? [{ panes: [ // map the current path to router panes ...path.map((id) => [{ id }]), // then augment with the intents IDs and params [{ id: params.id, params: otherParams, payload }] ], depthIndex: path.length, levelIndex }] : resolvedPane.type === "list" && resolvedPane.child && resolvedPane.items ? (await Promise.all(resolvedPane.items.map((item, nextLevelIndex) => item.type === "divider" ? Promise.resolve([]) : traverse({ currentId: item._id || item.id, flatIndex: flatIndex + 1, intent, params, parent: resolvedPane, path: [...path, item.id], payload, unresolvedPane: typeof resolvedPane.child == "function" ? memoBind(resolvedPane, "child") : resolvedPane.child, levelIndex: nextLevelIndex, structureContext })))).flat() : [] ); } const closestPaneToRoot = (await traverse({ currentId: "root", flatIndex: 0, levelIndex: 0, intent: options.intent, params: options.params, parent: null, path: [], payload: options.payload, unresolvedPane: options.rootPaneNode, structureContext: options.structureContext })).sort((a, b) => a.depthIndex === b.depthIndex ? a.levelIndex - b.levelIndex : a.depthIndex - b.depthIndex)[0]; return closestPaneToRoot ? closestPaneToRoot.panes : fallbackEditorPanes; } const fallbackEditorChild = (nodeId, context) => { const id = nodeId.replace(/^__edit__/, ""), { params, payload, structureContext: { resolveDocumentNode } } = context, { type, template } = params; if (!type) throw new Error(`Document type for document with ID ${id} was not provided in the router params.`); let defaultDocumentBuilder = resolveDocumentNode({ schemaType: type, documentId: id }).id("editor"); return template && (defaultDocumentBuilder = defaultDocumentBuilder.initialValueTemplate(template, payload)), defaultDocumentBuilder.serialize(); }; function hashContext(context) { return `contextHash(${JSON.stringify({ id: context.id, parentId: parent && assignId(parent), path: context.path, index: context.index, splitIndex: context.splitIndex, serializeOptionsIndex: context.serializeOptions?.index, serializeOptionsPath: context.serializeOptions?.path })})`; } const hashResolvedPaneMeta = (meta) => { const normalized = { type: meta.type, id: meta.routerPaneSibling.id, params: meta.routerPaneSibling.params || {}, payload: meta.routerPaneSibling.payload || null, flatIndex: meta.flatIndex, groupIndex: meta.groupIndex, siblingIndex: meta.siblingIndex, path: meta.path, paneNode: meta.type === "resolvedMeta" ? assignId(meta.paneNode) : null }; return `metaHash(${JSON.stringify(normalized)})`; }; function resolvePaneTree({ unresolvedPane, flattenedRouterPanes, parent: parent2, path, resolvePane, structureContext }) { const [current, ...rest] = flattenedRouterPanes, next = rest[0], context = { id: current.routerPaneSibling.id, splitIndex: current.siblingIndex, parent: parent2, path: [...path, current.routerPaneSibling.id], index: current.flatIndex, params: current.routerPaneSibling.params || {}, payload: current.routerPaneSibling.payload, structureContext }; try { return resolvePane(unresolvedPane, context, current.flatIndex).pipe( // this switch map receives a resolved pane switchMap((paneNode) => { const resolvedPaneMeta = { type: "resolvedMeta", ...current, paneNode, path: context.path }, loadingPanes = rest.map((i, restIndex) => ({ type: "loading", path: [...context.path, ...rest.slice(restIndex).map((_, currentIndex) => `[${i.flatIndex + currentIndex}]`)], paneNode: null, ...i })); if (!rest.length) return of([resolvedPaneMeta]); let nextStream; return ( /* the fallback editor case */ next?.routerPaneSibling.id.startsWith("__edit__") ? nextStream = resolvePaneTree({ unresolvedPane: fallbackEditorChild, flattenedRouterPanes: rest, parent: parent2, path: context.path, resolvePane, structureContext }) : current.groupIndex === next?.groupIndex ? nextStream = resolvePaneTree({ unresolvedPane, flattenedRouterPanes: rest, parent: parent2, path, resolvePane, structureContext }) : nextStream = resolvePaneTree({ unresolvedPane: typeof paneNode.child == "function" ? memoBind(paneNode, "child") : paneNode.child, flattenedRouterPanes: rest, parent: paneNode, path: context.path, resolvePane, structureContext }), concat( // we emit the loading panes first in a concat (this emits immediately) of([resolvedPaneMeta, ...loadingPanes]), // then whenever the next stream is done, the results will be combined. nextStream.pipe(map((nextResolvedPanes) => [resolvedPaneMeta, ...nextResolvedPanes])) ) ); }) ); } catch (e) { if (e instanceof PaneResolutionError && (e.context && console.warn(`Pane resolution error at index ${e.context.index}${e.context.splitIndex > 0 ? ` for split pane index ${e.context.splitIndex}` : ""}: ${e.message}${e.helpId ? ` - see ${generateHelpUrl(e.helpId)}` : ""}`, e), e.helpId === "structure-item-returned-no-child")) return of([]); throw e; } } function createResolvedPaneNodeStream({ routerPanesStream, rootPaneNode, initialCacheState = { cacheKeysByFlatIndex: [], flattenedRouterPanes: [], resolvedPaneCache: /* @__PURE__ */ new Map(), resolvePane: () => NEVER }, structureContext }) { return routerPanesStream.pipe( // add in implicit "root" router pane map((rawRouterPanes) => [[{ id: "root" }], ...rawRouterPanes]), // create flattened router panes map((routerPanes) => routerPanes.flatMap((routerPaneGroup, groupIndex) => routerPaneGroup.map((routerPaneSibling, siblingIndex) => ({ routerPaneSibling, groupIndex, siblingIndex }))).map((i, index) => ({ ...i, flatIndex: index }))), // calculate a "diffIndex" used for clearing the memo cache startWith([]), pairwise(), map(([prev, curr]) => { for (let i = 0; i < curr.length; i++) { const prevValue = prev[i], currValue = curr[i]; if (!isEqual(prevValue, currValue)) return { flattenedRouterPanes: curr, diffIndex: i }; } return { flattenedRouterPanes: curr, diffIndex: curr.length }; }), // create the memoized `resolvePane` function and manage the memo cache scan((acc, next) => { const { cacheKeysByFlatIndex, resolvedPaneCache } = acc, { flattenedRouterPanes, diffIndex } = next, beforeDiffIndex = cacheKeysByFlatIndex.slice(0, diffIndex + 1), afterDiffIndex = cacheKeysByFlatIndex.slice(diffIndex + 1), keysToKeep = new Set(beforeDiffIndex.flatMap((keySet) => Array.from(keySet))), keysToDelete = afterDiffIndex.flatMap((keySet) => Array.from(keySet)).filter((key) => !keysToKeep.has(key)); for (const key of keysToDelete) resolvedPaneCache.delete(key); return { flattenedRouterPanes, cacheKeysByFlatIndex, resolvedPaneCache, resolvePane: createPaneResolver((nextFn) => (unresolvedPane, context, flatIndex) => { const key = unresolvedPane && `${assignId(unresolvedPane)}-${hashContext(context)}`, cachedResolvedPane = key && resolvedPaneCache.get(key); if (cachedResolvedPane) return cachedResolvedPane; const result = nextFn(unresolvedPane, context, flatIndex); if (!key) return result; const cacheKeySet = cacheKeysByFlatIndex[flatIndex] || /* @__PURE__ */ new Set(); return cacheKeySet.add(key), cacheKeysByFlatIndex[flatIndex] = cacheKeySet, resolvedPaneCache.set(key, result), result; }) }; }, initialCacheState), // run the memoized, recursive resolving switchMap(({ flattenedRouterPanes, resolvePane }) => resolvePaneTree({ unresolvedPane: rootPaneNode, flattenedRouterPanes, parent: null, path: [], resolvePane, structureContext })) ).pipe( // this diffs the previous emission with the current one. if there is a new // loading pane at the same position where a previous pane already had a // resolved value (looking at the IDs to compare), then return the previous // pane instead of the loading pane scan((prev, next) => next.map((nextPane, index) => { const prevPane = prev[index]; return !prevPane || nextPane.type !== "loading" ? nextPane : prevPane.routerPaneSibling.id === nextPane.routerPaneSibling.id ? prevPane : nextPane; }), []), // this prevents duplicate emissions distinctUntilChanged((prev, next) => { if (prev.length !== next.length) return !1; for (let i = 0; i < next.length; i++) { const prevValue = prev[i], nextValue = next[i]; if (hashResolvedPaneMeta(prevValue) !== hashResolvedPaneMeta(nextValue)) return !1; } return !0; }) ); } function useRouterPanesStream() { const $ = c(6), [routerStateSubject] = useState(_temp$5); let t0, t1; $[0] !== routerStateSubject ? (t1 = routerStateSubject.asObservable().pipe(map(_temp2$3)), $[0] = routerStateSubject, $[1] = t1) : t1 = $[1], t0 = t1; const routerPanes$ = t0, { state: routerState } = useRouter(); let t2, t3; return $[2] !== routerState || $[3] !== routerStateSubject ? (t2 = () => { routerStateSubject.next(routerState); }, t3 = [routerState, routerStateSubject], $[2] = routerState, $[3] = routerStateSubject, $[4] = t2, $[5] = t3) : (t2 = $[4], t3 = $[5]), useEffect(t2, t3), routerPanes$; } function _temp2$3(_routerState) { return _routerState?.panes || []; } function _temp$5() { return new ReplaySubject(1); } function useResolvedPanes() { const $ = c(6), [error, setError] = useState(); if (error) throw error; const { structureContext, rootPaneNode } = useStructureTool(); let t0; $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = { paneDataItems: [], resolvedPanes: [], routerPanes: [] }, $[0] = t0) : t0 = $[0]; const [data, setData] = useState(t0), routerPanesStream = useRouterPanesStream(); let t1, t2; return $[1] !== rootPaneNode || $[2] !== routerPanesStream || $[3] !== structureContext ? (t1 = () => { const subscription = createResolvedPaneNodeStream({ rootPaneNode, routerPanesStream, structureContext }).pipe(map(_temp5)).subscribe({ next: (result) => setData(result), error: (e) => setError(e) }); return () => subscription.unsubscribe(); }, t2 = [rootPaneNode, routerPanesStream, structureContext], $[1] = rootPaneNode, $[2] = routerPanesStream, $[3] = structureContext, $[4] = t1, $[5] = t2) : (t1 = $[4], t2 = $[5]), useEffect(t1, t2), data; } function _temp5(resolvedPanes) { const routerPanes = resolvedPanes.reduce(_temp3, []), groupsLen = routerPanes.length, paneDataItems = resolvedPanes.map((pane) => { const { groupIndex, flatIndex, siblingIndex, routerPaneSibling, path } = pane, itemId = routerPaneSibling.id, nextGroup = routerPanes[groupIndex + 1]; return { active: groupIndex === groupsLen - 2, childItemId: nextGroup?.[0].id ?? null, index: flatIndex, itemId: routerPaneSibling.id, groupIndex, key: `${pane.type === "loading" ? "unknown" : pane.paneNode.id}-${itemId}-${siblingIndex}`, pane: pane.type === "loading" ? LOADING_PANE : pane.paneNode, params: routerPaneSibling.params || {}, path: path.join(";"), payload: routerPaneSibling.payload, selected: flatIndex === resolvedPanes.length - 1, siblingIndex }; }); return { paneDataItems, routerPanes, resolvedPanes: paneDataItems.map(_temp4) }; } function _temp4(pane_0) { return pane_0.pane; } function _temp3(acc, next) { const currentGroup = acc[next.groupIndex] || []; return currentGroup[next.siblingIndex] = next.routerPaneSibling, acc[next.groupIndex] = currentGroup, acc; } async function ensureDocumentIdAndType(documentStore, id, type) { if (id && type) return { id, type }; if (!id && type) return { id: uuid(), type }; if (id && !type) { const resolvedType = await firstValueFrom(documentStore.resolveTypeForDocument(id)); return { id, type: resolvedType }; } throw new PaneResolutionError({ message: "Neither document `id` or `type` was provided when trying to resolve intent." }); } const EMPTY_RECORD = {}, IntentResolver = memo(function() { const $ = c(7), { navigate } = useRouter(), maybeIntent = useRouterState(_temp$4), { rootPaneNode, structureContext } = useStructureTool(), documentStore = useDocumentStore(), [error, setError] = useState(null); if (error) throw error; let t0, t1; return $[0] !== documentStore || $[1] !== maybeIntent || $[2] !== navigate || $[3] !== rootPaneNode || $[4] !== structureContext ? (t0 = () => { if (maybeIntent) { const { intent, params, payload } = maybeIntent; let cancelled = !1; return async function() { const { id, type } = await ensureDocumentIdAndType(documentStore, typeof params.id == "string" ? params.id : void 0, typeof params.type == "string" ? params.type : void 0); if (cancelled) return; const panes = await resolveIntent({ intent, params: { ...params, id, type }, payload, rootPaneNode, structureContext }); cancelled || navigate({ panes }, { replace: !0 }); }().catch(setError), () => { cancelled = !0; }; } }, t1 = [documentStore, maybeIntent, navigate, rootPaneNode, structureContext], $[0] = documentStore, $[1] = maybeIntent, $[2] = navigate, $[3] = rootPaneNode, $[4] = structureContext, $[5] = t0, $[6] = t1) : (t0 = $[5], t1 = $[6]), useEffect(t0, t1), null; }); function _temp$4(routerState) { const intentName = typeof routerState.intent == "string" ? routerState.intent : void 0; return intentName ? { intent: intentName, params: isRecord(routerState.params) ? routerState.params : EMPTY_RECORD, payload: routerState.payload } : void 0; } const PathSegment = styled.span` &:not(:last-child)::after { content: ' ➝ '; opacity: 0.5; } `; function formatStack(stack) { return stack.replace(/\(\.\.\.\)\./g, `(...) .`).replace(/__WEBPACK_IMPORTED_MODULE_\d+_+/g, "").replace(/___default\./g, ".").replace(new RegExp(` \\(https?:\\/\\/${window.location.host}`, "g"), " ("); } function StructureError(t0) { const $ = c(37), { error } = t0; if (!(error instanceof PaneResolutionError)) throw error; const { cause } = error, { t } = useTranslation(structureLocaleNamespace), stack = cause?.stack || error.stack, showStack = stack && !(cause instanceof SerializeError) && !error.message.includes("Module build failed:"); let t1; $[0] !== cause ? (t1 = cause instanceof SerializeError ? cause.path : [], $[0] = cause, $[1] = t1) : t1 = $[1]; const path = t1, helpId = cause instanceof SerializeError && cause.helpId || error.helpId, handleReload = _temp$3; let t2; $[2] !== t ? (t2 = t("structure-error.header.text"), $[2] = t, $[3] = t2) : t2 = $[3]; let t3; $[4] !== t2 ? (t3 = /* @__PURE__ */ jsx(Heading, { as: "h2", children: t2 }), $[4] = t2, $[5] = t3) : t3 = $[5]; let t4; $[6] !== path || $[7] !== t ? (t4 = path.length > 0 && /* @__PURE__ */ jsxs(Stack, { space: 2, children: [ /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: t("structure-error.structure-path.label") }), /* @__PURE__ */ jsx(Code, { children: path.slice(1).map(_temp2$2) }) ] }), $[6] = path, $[7] = t, $[8] = t4) : t4 = $[8]; let t5; $[9] !== t ? (t5 = t("structure-error.error.label"), $[9] = t, $[10] = t5) : t5 = $[10]; let t6; $[11] !== t5 ? (t6 = /* @__PURE__ */ jsx(Text, { size: 1, weight: "medium", children: t5 }), $[11] = t5, $[12] = t6) : t6 = $[12]; let t7; $[13] !== error.message || $[14] !== showStack || $[15] !== stack ? (t7 = showStack ? formatStack(stack) : error.message, $[13] = error.message, $[14] = showStack, $[15] = stack, $[16] = t7) : t7 = $[16]; let t8; $[17] !== t7 ? (t8 = /* @__PURE__ */ jsx(Code, { children: t7 }), $[17] = t7, $[18] = t8) : t8 = $[18]; let t9; $[19] !== t6 || $[20] !== t8 ? (t9 = /* @__PURE__ */ jsxs(Stack, { marginTop: 4, space: 2, children: [ t6, t8 ] }), $[19] = t6, $[20] = t8, $[21] = t9) : t9 = $[21]; let t10; $[22] !== helpId || $[23] !== t ? (t10 = helpId && /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsx(Text, { children: /* @__PURE__ */ jsx("a", { href: generateHelpUrl(helpId), rel: "noopener noreferrer", target: "_blank", children: t("structure-error.docs-link.text") }) }) }), $[22] = helpId, $[23] = t, $[24] = t10) : t10 = $[24]; let t11; $[25] !== t ? (t11 = t("structure-error.reload-button.text"), $[25] = t, $[26] = t11) : t11 = $[26]; let t12; $[27] !== t11 ? (t12 = /* @__PURE__ */ jsx(Box, { marginTop: 4, children: /* @__PURE__ */ jsx(Button, { text: t11, icon: SyncIcon, tone: "primary", onClick: handleReload }) }), $[27] = t11, $[28] = t12) : t12 = $[28]; let t13; $[29] !== t10 || $[30] !== t12 || $[31] !== t4 || $[32] !== t9 ? (t13 = /* @__PURE__ */ jsxs(Card, { marginTop: 4, padding: 4, radius: 2, overflow: "auto", shadow: 1, tone: "inherit", children: [ t4, t9, t10, t12 ] }), $[29] = t10, $[30] = t12, $[31] = t4, $[32] = t9, $[33] = t13) : t13 = $[33]; let t14; return $[34] !== t13 || $[35] !== t3 ? (t14 = /* @__PURE__ */ jsx(Card, { height: "fill", overflow: "auto", padding: 4, sizing: "border", tone: "critical", children: /* @__PURE__ */ jsxs(Container, { children: [ t3, t13 ] }) }), $[34] = t13, $[35] = t3, $[36] = t14) : t14 = $[36], t14; } function _temp2$2(segment, i) { return /* @__PURE__ */ jsx(PathSegment, { children: segment }, `${segment}-${i}`); } function _temp$3() { window.location.reload(); } function NoDocumentTypesScreen() { const $ = c(17), { t } = useTranslation(structureLocaleNamespace); let t0; $[0] === Symbol.for("react.memo_cache_sentinel") ? (t0 = /* @__PURE__ */ jsx(Box, { children: /* @__PURE__ */ jsx(Text, { size: 1, children: /* @__PURE__ */ jsx(WarningOutlineIcon, {}) }) }), $[0] = t0) : t0 = $[0]; let t1; $[1] !== t ? (t1 = t("no-document-types-screen.title"), $[1] = t, $[2] = t1) : t1 = $[2]; let t2; $[3] !== t1 ? (t2 = /* @__PURE__ */ jsx(Text, { as: "h1", size: 1, weight: "medium", children: t1 }), $[3] = t1, $[4] = t2) : t2 = $[4]; let t3; $[5] !== t ? (t3 = t("no-document-types-screen.subtitle"), $[5] = t, $[6] = t3) : t3 = $[6]; let t4; $[7] !== t3 ? (t4 = /* @__PURE__ */ jsx(Text, { as: "p", muted: !0, size: 1, children: t3 }), $[7] = t3, $[8] = t4) : t4 = $[8]; let t5; $[9] !== t ? (t5 = t("no-document-types-screen.link-text"), $[9] = t, $[10] = t5) : t5 = $[10]; let t6; $[11] !== t5 ? (t6 = /* @__PURE__ */ jsx(Text, { as: "p", muted: !0, size: 1, children: /* @__PURE__ */ jsx("a", { href: "https://www.sanity.io/docs/create-a-schema-and-configure-sanity-studio", target: "_blank", rel: "noreferrer", children: t5 }) }), $[11] = t5, $[12] = t6) : t6 = $[12]; let t7; return $[13] !== t2 || $[14] !== t4 || $[15] !== t6 ? (t7 = /* @__PURE__ */ jsx(Card, { height: "fill", children: /* @__PURE__ */ jsx(Flex, { align: "center", height: "fill", justify: "center", padding: 4, sizing: "border", children: /* @__PURE__ */ jsx(Container, { width: 0, children: /* @__PURE__ */ jsx(Card, { padding: 4, radius: 2, shadow: 1, tone: "caution", children: /* @__PURE__ */ jsxs(Flex, { children: [ t0, /* @__PURE__ */ jsxs(Stack, { flex: 1, marginLeft: 3, space: 3, children: [ t2, t4, t6 ] }) ] }) }) }) }) }), $[13] = t2, $[14] = t4, $[15] = t6, $[16] = t7) : t7 = $[16], t7; } const DocumentTitle = (props) => { const $ = c(7), { documentId, documentType } = props, { selectedReleaseId } = usePerspective(), editState = useEditState(documentId, documentType, "default", selectedReleaseId), schema = useSchema(), { t } = useTranslation(structureLocaleNamespace), isNewDocument = !editState?.published && !editState?.draft, documentValue = editState?.version || editState?.draft || editState?.published, schemaType = schema.get(documentType), { value, isLoading: previewValueIsLoading } = unstable_useValuePreview({ enabled: !!documentValue, schemaType, value: documentValue }), { lastRevisionDocument } = useDocumentLastRev(documentId, documentType), documentTitle = lastRevisionDocument && !documentValue ? "" : isNewDocument ? t("browser-document-title.new-document", { schemaType: schemaType?.title || schemaType?.name }) : value?.title || t("browser-document-title.untitled-document"), settled = editState.ready && !previewValueIsLoading, newTitle = useConstructDocumentTitle(documentTitle); let t0; $[0] !== newTitle || $[1] !== settled ? (t0 = () => { settled && (document.title = newTitle); }, $[0] = newTitle, $[1] = settled, $[2] = t0) : t0 = $[2]; let t1; return $[3] !== documentTitle || $[4] !== newTitle || $[5] !== settled ? (t1 = [documentTitle, settled, newTitle], $[3] = documentTitle, $[4] = newTitle, $[5] = settled, $[6] = t1) : t1 = $[6], useEffect(t0, t1), null; }, PassthroughTitle = (props) => { const $ = c(5), { title } = props, newTitle = useConstructDocumentTitle(title); let t0; $[0] !== newTitle ? (t0 = () => { document.title = newTitle; }, $[0] = newTitle, $[1] = t0) : t0 = $[1]; let t1; return $[2] !== newTitle || $[3] !== title ? (t1 = [newTitle, title], $[2] = newTitle, $[3] = title, $[4] = t1) : t1 = $[4], useEffect(t0, t1), null; }, StructureTitle = (props) => { const $ = c(8), { resolvedPanes } = props; if (!resolvedPanes?.length) return null; const lastPane = resolvedPanes[resolvedPanes.length - 1]; if (isLoadingPane(lastPane)) { let t02; return $[0] === Symbol.for("react.memo_cache_sentinel") ? (t02 = /* @__PURE__ */ jsx(PassthroughTitle, {}), $[0] = t02) : t02 = $[0], t02; } if (isDocumentPane(lastPane)) { if (lastPane?.title) { let t03; return $[1] !== lastPane.title ? (t03 = /* @__PURE__ */ jsx(PassthroughTitle, { title: lastPane.title }), $[1] = lastPane.title, $[2] = t03) : t03 = $[2], t03; } let t02; return $[3] !== lastPane.options.id || $[4] !== lastPane.options.type ? (t02 = /* @__PURE__ */ jsx(DocumentTitle, { documentId: lastPane.options.id, documentType: lastPane.options.type }), $[3] = lastPane.options.id, $[4] = lastPane.options.type, $[5] = t02) : t02 = $[5], t02; } const t0 = lastPane?.title; let t1; return $[6] !== t0 ? (t1 = /* @__PURE__ */ jsx(PassthroughTitle, { title: t0 }), $[6] = t0, $[7] = t1) : t1 = $[7], t1; }; function useConstructDocumentTitle(activeTitle) { const $ = c(3), structureToolBaseTitle = useStructureTool().structureContext.title; let t0; return $[0] !== activeTitle || $[1] !== structureToolBaseTitle ? (t0 = [activeTitle, structureToolBaseTitle].filter(_temp$2), $[0] = activeTitle, $[1] = structureToolBaseTitle, $[2] = t0) : t0 = $[2], t0.join(" | "); } function _temp$2(title) { return title; } function isDocumentPane(pane) { return pane !== LOADING_PANE && pane.type === "document"; } function isLoadingPane(pane) { return pane === LOADING_PANE; } const StyledPaneLayout = styled(PaneLayout)` min-height: 100%; min-width: 320px; `, isSaveHotkey = isHotkey("mod+s"), StructureTool = memo(function(t0) { const $ = c(31), { onPaneChange } = t0, { push: pushToast } = useToast(), schema = useSchema(), { layoutCollapsed, setLayoutCollapsed } = useStructureTool(), { paneDataItems, resolvedPanes } = useResolvedPanes(), isResolvingIntent = useRouterState(_temp$1), { sanity: t1 } = useTheme(), { media } = t1, [portalElement, setPortalElement] = useState(null); let t2; $[0] !== setLayoutCollapsed ? (t2 = () => setLayoutCollapsed(!0), $[0] = setLayoutCollapsed, $[1] = t2) : t2 = $[1]; const handleRootCollapse = t2; let t3; $[2] !== setLayoutCollapsed ? (t3 = () => setLayoutCollapsed(!1), $[2] = setLayoutCollapsed, $[3] = t3) : t3 = $[3]; const handleRootExpand = t3; let t4, t5; $[4] !== onPaneChange || $[5] !== resolvedPanes ? (t4 = () => { resolvedPanes.length && onPaneChange(resolvedPanes); }, t5 = [onPaneChange, resolvedPanes], $[4] = onPaneChange, $[5] = resolvedPanes, $[6] = t4, $[7] = t5) : (t4 = $[6], t5 = $[7]), useEffect(t4, t5); let t6, t7; if ($[8] !== pushToast ? (t6 = () => { const handleGlobalKeyDown = (event) => { isSaveHotkey(event) && (event.preventDefault(), pushToast({ closable: !0, id: "auto-save-message", status: "info", title: "Your work is automatically saved!", duration: 4e3 })); }; return window.addEventListener("keydown", handleGlobalKeyDown), () => window.removeEventListener("keydown", handleGlobalKeyDown); }, t7 = [pushToast], $[8] = pushToast, $[9] = t6, $[10] = t7) : (t6 = $[9], t7 = $[10]), useEffect(t6, t7), !schema._original?.types.some(_isCustomDocumentTypeDefinition)) { let t82; return $[11] === Symbol.for("react.memo_cache_sentinel") ? (t82 = /* @__PURE__ */ jsx(NoDocumentTypesScreen, {}), $[11] = t82) : t82 = $[11], t82; } const t8 = portalElement || null, t9 = layoutCollapsed ? void 0 : "fill", t10 = media[1]; let t11; $[12] !== paneDataItems ? (t11 = paneDataItems.map(_temp2$1), $[12] = paneDataItems, $[13] = t11) : t11 = $[13]; let t12; $[14] !== isResolvingIntent || $[15] !== paneDataItems.length ? (t12 = paneDataItems.length <= 1 && isResolvingIntent && /* @__PURE__ */ jsx(LoadingPane, { paneKey: "intent-resolver" }), $[14] = isResolvingIntent, $[15] = paneDataItems.length, $[16] = t12) : t12 = $[16]; let t13; $[17] !== handleRootCollapse || $[18] !== handleRootExpand || $[19] !== media[1] || $[20] !== t11 || $[21] !== t12 || $[22] !== t9 ? (t13 = /* @__PURE__ */ jsxs(StyledPaneLayout, { flex: 1, height: t9, minWidth: t10, onCollapse: handleRootCollapse, onExpand: handleRootExpand, children: [ t11, t12 ] }), $[17] = handleRootCollapse, $[18] = handleRootExpand, $[19] = media[1], $[20] = t11, $[21] = t12, $[22] = t9, $[23] = t13) : t13 = $[23]; let t14; $[24] !== resolvedPanes ? (t14 = /* @__PURE__ */ jsx(StructureTitle, { resolvedPanes }), $[24] = resolvedPanes, $[25] = t14) : t14 = $[25]; let t15; $[26] === Symbol.for("react.memo_cache_sentinel") ? (t15 = /* @__PURE__ */ jsx("div", { "data-portal": "", ref: setPortalElement }), $[26] = t15) : t15 = $[26]; let t16; return $[27] !== t13 || $[28] !== t14 || $[29] !== t8 ? (t16 = /* @__PURE__ */ jsxs(PortalProvider, { element: t8, children: [ t13, t14, t15 ] }), $[27] = t13, $[28] = t14, $[29] = t8, $[30] = t16) : t16 = $[30], t16; }); function _temp$1(routerState) { return typeof routerState.intent == "string"; } function _temp2$1(t0) { const { active, childItemId, groupIndex, itemId, key: paneKey, pane, index: paneIndex, params: paneParams, path, payload, siblingIndex, selected } = t0; return /* @__PURE__ */ jsx(Fragment, { children: pane === LOADING_PANE ? /* @__PURE__ */ jsx(LoadingPane, { paneKey, path, selected }) : /* @__PURE__ */ jsx(StructureToolPane, { active, groupIndex, index: paneIndex, pane, childItemId, itemId, paneKey, params: paneParams, payload, path, selected, siblingIndex }) }, `${pane === LOADING_PANE ? "loading" : pane.type}-${paneIndex}`); } function StructureToolBoundary(t0) { const $ = c(14), { tool: t1 } = t0, { options } = t1, { unstable_sources: sources } = useWorkspace(), [firstSource] = sources; let t2; $[0] !== options ? (t2 = options || {}, $[0] = options, $[1] = t2) : t2 = $[1]; const { source, defaultDocumentNode, structure } = t2; let t3; $[2] === Symbol.for("react.memo_cache_sentinel") ? (t3 = [], $[2] = t3) : t3 = $[2], useEffect(_temp2, t3); let t4; $[3] === Symbol.for("react.memo_cache_sentinel") ? (t4 = { error: null }, $[3] = t4) : t4 = $[3]; const [t5, setError] = useState(t4), { error } = t5; if (error) { let t62; return $[4] !== error ? (t62 = /* @__PURE__ */ jsx(StructureError, { error }), $[4] = error, $[5] = t62) : t62 = $[5], t62; } const t6 = source || firstSource.name; let t7, t8; $[6] === Symbol.for("react.memo_cache_sentinel") ? (t7 = /* @__PURE__ */ jsx(StructureTool, { onPaneChange: setActivePanes }), t8 = /* @__PURE__ */ jsx(IntentResolver, {}), $[6] = t7, $[7] = t8) : (t7 = $[6], t8 = $[7]); let t9; $[8] !== defaultDocumentNode || $[9] !== structure ? (t9 = /* @__PURE__ */ jsxs(StructureToolProvider, { defaultDocumentNode, structure, children: [ t7, t8 ] }), $[8] = defaultDocumentNode, $[9] = structure, $[10] = t9) : t9 = $[10]; let t10; return $[11] !== t6 || $[12] !== t9 ? (t10 = /* @__PURE__ */ jsx(ErrorBoundary, { onCatch: setError, children: /* @__PURE__ */ jsx(SourceProvider, { name: t6, children: t9 }) }), $[11] = t6, $[12] = t9, $[13] = t10) : t10 = $[13], t10; } function _temp2() { return setActivePanes([]), _temp; } function _temp() { return setActivePanes([]); } export { StructureToolBoundary as default }; //# sourceMappingURL=index3.mjs.map