import {
  Capabilities,
  Events,
  PluginManager
} from "./chunk-WPW5VVAX.mjs";

// src/react/useCapabilities.ts
import { useAtomValue } from "@effect-atom/atom-react";
import { invariant } from "@dxos/invariant";

// src/react/PluginManagerProvider.ts
import { createContext, useContext } from "react";
import { raise } from "@dxos/debug";
var PluginManagerContext = createContext(void 0);
var usePluginManager = () => useContext(PluginManagerContext) ?? raise(new Error("Missing PluginManagerContext"));
var PluginManagerProvider = PluginManagerContext.Provider;

// src/react/useCapabilities.ts
var __dxlog_file = "/__w/dxos/dxos/packages/sdk/app-framework/src/react/useCapabilities.ts";
var useCapabilities = (interfaceDef) => {
  const manager = usePluginManager();
  return useAtomValue(manager.context.capabilities(interfaceDef));
};
var useCapability = (interfaceDef) => {
  const capabilities = useCapabilities(interfaceDef);
  invariant(capabilities.length > 0, `No capability found for ${interfaceDef.identifier}`, {
    F: __dxlog_file,
    L: 29,
    S: void 0,
    A: [
      "capabilities.length > 0",
      "`No capability found for ${interfaceDef.identifier}`"
    ]
  });
  return capabilities[0];
};

// src/react/common.ts
var useIntentDispatcher = () => useCapability(Capabilities.IntentDispatcher);
var useAppGraph = () => useCapability(Capabilities.AppGraph);
var useLayout = () => useCapability(Capabilities.Layout);

// src/react/types.ts
import * as Schema from "effect/Schema";
var SurfaceCardRole = Schema.Literal("card", "card--popover", "card--intrinsic", "card--extrinsic", "card--transclusion");

// src/react/ErrorBoundary.tsx
import { useSignals as _useSignals } from "@preact-signals/safe-react/tracking";
import React, { Component } from "react";
var ErrorBoundary = class extends Component {
  static getDerivedStateFromError(error) {
    return {
      error
    };
  }
  state = {
    error: void 0
  };
  componentDidUpdate(prevProps) {
    if (prevProps.data !== this.props.data) {
      this.resetError();
    }
  }
  render() {
    if (this.state.error) {
      const Fallback = this.props.fallback ?? DefaultFallback;
      return /* @__PURE__ */ React.createElement(Fallback, {
        data: this.props.data,
        error: this.state.error
      });
    }
    return this.props.children;
  }
  resetError() {
    this.setState({
      error: void 0
    });
  }
};
var DefaultFallback = ({ data, error }) => {
  var _effect = _useSignals();
  try {
    return /* @__PURE__ */ React.createElement("div", {
      className: "flex flex-col gap-2 overflow-hidden border border-red-500 rounded-sm"
    }, /* @__PURE__ */ React.createElement("h1", {
      className: "p-2"
    }, "ERROR: ", error.message), /* @__PURE__ */ React.createElement("pre", {
      className: "p-2 overflow-y-auto text-sm text-subdued"
    }, JSON.stringify(data, null, 2)));
  } finally {
    _effect.f();
  }
};

// src/react/Surface.tsx
import { useSignals as _useSignals2 } from "@preact-signals/safe-react/tracking";
import React2, { Fragment, Suspense, forwardRef, memo, useMemo } from "react";
import { useDefaultValue } from "@dxos/react-hooks";
import { byPosition } from "@dxos/util";
var DEFAULT_PLACEHOLDER = /* @__PURE__ */ React2.createElement(Fragment, null);
var Surface = /* @__PURE__ */ memo(/* @__PURE__ */ forwardRef(({ id: _id, role, data: dataParam, limit, fallback = DefaultFallback2, placeholder = DEFAULT_PLACEHOLDER, ...rest }, forwardedRef) => {
  var _effect = _useSignals2();
  try {
    const surfaces = useSurfaces();
    const data = useDefaultValue(dataParam, () => ({}));
    const definitions = findCandidates(surfaces, {
      role,
      data
    });
    const candidates = limit ? definitions.slice(0, limit) : definitions;
    const nodes = candidates.map(({ id, component: Component2 }) => /* @__PURE__ */ React2.createElement(Component2, {
      ref: forwardedRef,
      key: id,
      id,
      role,
      data,
      limit,
      ...rest
    }));
    const suspense = /* @__PURE__ */ React2.createElement(Suspense, {
      fallback: placeholder
    }, nodes);
    return /* @__PURE__ */ React2.createElement(ErrorBoundary, {
      data,
      fallback
    }, suspense);
  } finally {
    _effect.f();
  }
}));
var DefaultFallback2 = ({ data, error, dev }) => {
  var _effect = _useSignals2();
  try {
    if (dev) {
      return /* @__PURE__ */ React2.createElement("div", {
        className: "flex flex-col gap-4 p-4 is-full overflow-y-auto"
      }, /* @__PURE__ */ React2.createElement("h1", {
        className: "flex gap-2 text-sm mbs-2"
      }, error.message), /* @__PURE__ */ React2.createElement("pre", {
        className: "overflow-auto text-xs text-description"
      }, JSON.stringify(data, null, 2)));
    }
    return /* @__PURE__ */ React2.createElement("div", {
      className: "flex flex-col gap-4 p-4 is-full overflow-y-auto border border-roseFill"
    }, /* @__PURE__ */ React2.createElement("h1", {
      className: "flex gap-2 text-sm mbs-2 text-rose-500"
    }, error.message), /* @__PURE__ */ React2.createElement("pre", {
      className: "overflow-auto text-xs text-description"
    }, error.stack), /* @__PURE__ */ React2.createElement("pre", {
      className: "overflow-auto text-xs text-description"
    }, JSON.stringify(data, null, 2)));
  } finally {
    _effect.f();
  }
};
var useSurfaces = () => {
  const surfaces = useCapabilities(Capabilities.ReactSurface);
  return useMemo(() => surfaces.flat(), [
    surfaces
  ]);
};
var isSurfaceAvailable = (context, { role, data }) => {
  const surfaces = context.getCapabilities(Capabilities.ReactSurface);
  const candidates = findCandidates(surfaces.flat(), {
    role,
    data
  });
  return candidates.length > 0;
};
var findCandidates = (surfaces, { role, data }) => {
  return Object.values(surfaces).filter((definition) => Array.isArray(definition.role) ? definition.role.includes(role) : definition.role === role).filter(({ filter }) => filter ? filter(data ?? {}) : true).toSorted(byPosition);
};
Surface.displayName = "Surface";

// src/react/useApp.tsx
import { RegistryContext } from "@effect-atom/atom-react";
import { effect } from "@preact/signals-core";
import React5, { useCallback, useEffect as useEffect2, useMemo as useMemo2 } from "react";
import { invariant as invariant2 } from "@dxos/invariant";
import { live } from "@dxos/live-object";
import { useAsyncEffect, useDefaultValue as useDefaultValue2 } from "@dxos/react-hooks";

// src/react/App.tsx
import { useSignals as _useSignals3 } from "@preact-signals/safe-react/tracking";
import React3 from "react";

// src/helpers.ts
var topologicalSort = (nodes) => {
  const getDependencies = (nodeId, seen = /* @__PURE__ */ new Set(), path = /* @__PURE__ */ new Set()) => {
    if (path.has(nodeId)) {
      throw new Error(`Circular dependency detected involving ${nodeId}`);
    }
    if (seen.has(nodeId)) {
      return [];
    }
    const node = nodes.find((n) => n.id === nodeId);
    if (!node) {
      throw new Error(`Node ${nodeId} not found but is listed as a dependency`);
    }
    const newPath = /* @__PURE__ */ new Set([
      ...path,
      nodeId
    ]);
    const newSeen = /* @__PURE__ */ new Set([
      ...seen,
      nodeId
    ]);
    const dependsOn = node.dependsOn ?? [];
    return [
      ...dependsOn.flatMap((depId) => getDependencies(depId, newSeen, newPath)),
      nodeId
    ];
  };
  const allDependencies = nodes.map((node) => node.id).flatMap((id) => getDependencies(id)).filter((id, index, self) => self.indexOf(id) === index);
  return allDependencies.map((id) => nodes.find((node) => node.id === id)).filter((node) => node !== void 0);
};

// src/react/useLoading.tsx
import { useEffect, useState } from "react";
var LoadingState = /* @__PURE__ */ (function(LoadingState2) {
  LoadingState2[LoadingState2["Loading"] = 0] = "Loading";
  LoadingState2[LoadingState2["FadeIn"] = 1] = "FadeIn";
  LoadingState2[LoadingState2["FadeOut"] = 2] = "FadeOut";
  LoadingState2[LoadingState2["Done"] = 3] = "Done";
  return LoadingState2;
})({});
var useLoading = (state, debounce = 0) => {
  const [stage, setStage] = useState(0);
  useEffect(() => {
    if (!debounce) {
      return;
    }
    const i = setInterval(() => {
      setStage((stage2) => {
        switch (stage2) {
          case 0: {
            if (!state.ready) {
              return 1;
            } else {
              clearInterval(i);
              return 3;
            }
          }
          case 1: {
            if (state.ready) {
              return 2;
            }
            break;
          }
          case 2: {
            clearInterval(i);
            return 3;
          }
        }
        return stage2;
      });
    }, debounce);
    return () => clearInterval(i);
  }, [
    debounce
  ]);
  if (!debounce) {
    return state.ready ? 3 : 0;
  }
  return stage;
};

// src/react/App.tsx
var App = ({ placeholder: Placeholder, state, debounce }) => {
  var _effect = _useSignals3();
  try {
    const reactContexts = useCapabilities(Capabilities.ReactContext);
    const reactRoots = useCapabilities(Capabilities.ReactRoot);
    const stage = useLoading(state, debounce);
    if (state.error) {
      throw state.error;
    }
    if (stage < LoadingState.Done) {
      if (!Placeholder) {
        return null;
      }
      return /* @__PURE__ */ React3.createElement(Placeholder, {
        stage
      });
    }
    const ComposedContext = composeContexts(reactContexts);
    return /* @__PURE__ */ React3.createElement(ComposedContext, null, reactRoots.map(({ id, root: Component2 }) => /* @__PURE__ */ React3.createElement(Component2, {
      key: id
    })));
  } finally {
    _effect.f();
  }
};
var composeContexts = (contexts) => {
  if (contexts.length === 0) {
    return ({ children }) => /* @__PURE__ */ React3.createElement(React3.Fragment, null, children);
  }
  return topologicalSort(contexts).map(({ context }) => context).reduce((Acc, Next) => ({ children }) => /* @__PURE__ */ React3.createElement(Acc, null, /* @__PURE__ */ React3.createElement(Next, null, children)));
};

// src/react/DefaultFallback.tsx
import { useSignals as _useSignals4 } from "@preact-signals/safe-react/tracking";
import React4 from "react";
var DefaultFallback3 = ({ error }) => {
  var _effect = _useSignals4();
  try {
    return /* @__PURE__ */ React4.createElement("div", {
      style: {
        margin: "1rem",
        padding: "1rem",
        overflow: "hidden",
        border: "4px solid teal",
        borderRadius: "1rem"
      }
    }, /* @__PURE__ */ React4.createElement("h1", {
      style: {
        margin: "0.5rem 0",
        fontSize: "1.2rem"
      }
    }, "ERROR: ", error.message), /* @__PURE__ */ React4.createElement("pre", {
      style: {
        overflow: "auto",
        fontSize: "1rem",
        whiteSpace: "pre-wrap",
        color: "#888888"
      }
    }, error.stack));
  } finally {
    _effect.f();
  }
};

// src/react/useApp.tsx
var __dxlog_file2 = "/__w/dxos/dxos/packages/sdk/app-framework/src/react/useApp.tsx";
var ENABLED_KEY = "dxos.org/app-framework/enabled";
var useApp = ({ pluginManager, pluginLoader: pluginLoaderParam, plugins: pluginsParam, core: coreParam, defaults: defaultsParam, placeholder, fallback = DefaultFallback3, cacheEnabled = false, safeMode = false, debounce = 0 }) => {
  const plugins = useDefaultValue2(pluginsParam, () => []);
  const core = useDefaultValue2(coreParam, () => plugins.map(({ meta }) => meta.id));
  const defaults = useDefaultValue2(defaultsParam, () => []);
  const pluginLoader = useMemo2(() => pluginLoaderParam ?? ((id) => {
    const plugin = plugins.find((plugin2) => plugin2.meta.id === id);
    invariant2(plugin, `Plugin not found: ${id}`, {
      F: __dxlog_file2,
      L: 83,
      S: void 0,
      A: [
        "plugin",
        "`Plugin not found: ${id}`"
      ]
    });
    return plugin;
  }), [
    pluginLoaderParam,
    plugins
  ]);
  const state = useMemo2(() => live({
    ready: false,
    error: null
  }), []);
  const cached = useMemo2(() => JSON.parse(localStorage.getItem(ENABLED_KEY) ?? "[]"), []);
  const enabled = useMemo2(() => safeMode ? [] : cacheEnabled && cached.length > 0 ? cached : defaults, [
    safeMode,
    cacheEnabled,
    cached,
    defaults
  ]);
  const manager = useMemo2(() => pluginManager ?? new PluginManager({
    pluginLoader,
    plugins,
    core,
    enabled
  }), [
    pluginManager,
    pluginLoader,
    plugins,
    core,
    enabled
  ]);
  useEffect2(() => {
    return manager.activation.on(({ event, state: _state, error }) => {
      if (!state.ready && event === Events.Startup.id) {
        state.ready = _state === "activated";
      }
      if (error && !state.ready && !state.error) {
        state.error = error;
      }
    });
  }, [
    manager,
    state
  ]);
  useEffect2(() => {
    effect(() => {
      cacheEnabled && localStorage.setItem(ENABLED_KEY, JSON.stringify(manager.enabled));
    });
  }, [
    cacheEnabled,
    manager
  ]);
  useEffect2(() => {
    setupDevtools(manager);
  }, [
    manager
  ]);
  useAsyncEffect(async () => {
    manager.context.contributeCapability({
      interface: Capabilities.PluginManager,
      implementation: manager,
      module: "dxos.org/app-framework/plugin-manager"
    });
    manager.context.contributeCapability({
      interface: Capabilities.AtomRegistry,
      implementation: manager.registry,
      module: "dxos.org/app-framework/atom-registry"
    });
    await Promise.all([
      // TODO(wittjosiah): Factor out such that this could be called per surface role when attempting to render.
      manager.activate(Events.SetupReactSurface),
      manager.activate(Events.Startup)
    ]);
    return () => {
      manager.context.removeCapability(Capabilities.PluginManager, manager);
      manager.context.removeCapability(Capabilities.AtomRegistry, manager.registry);
    };
  }, [
    manager
  ]);
  return useCallback(() => /* @__PURE__ */ React5.createElement(ErrorBoundary, {
    fallback
  }, /* @__PURE__ */ React5.createElement(PluginManagerProvider, {
    value: manager
  }, /* @__PURE__ */ React5.createElement(RegistryContext.Provider, {
    value: manager.registry
  }, /* @__PURE__ */ React5.createElement(App, {
    placeholder,
    state,
    debounce
  })))), [
    fallback,
    manager,
    placeholder,
    state
  ]);
};
var setupDevtools = (manager) => {
  globalThis.composer ??= {};
  globalThis.composer.manager = manager;
};

// src/react/useIntentResolver.ts
import { useEffect as useEffect3 } from "react";
var useIntentResolver = (module, resolver) => {
  const manager = usePluginManager();
  useEffect3(() => {
    manager.context.contributeCapability({
      module,
      interface: Capabilities.IntentResolver,
      implementation: resolver
    });
    return () => manager.context.removeCapability(Capabilities.IntentResolver, resolver);
  }, [
    module,
    resolver
  ]);
};

export {
  usePluginManager,
  PluginManagerProvider,
  useCapabilities,
  useCapability,
  useIntentDispatcher,
  useAppGraph,
  useLayout,
  SurfaceCardRole,
  ErrorBoundary,
  Surface,
  useSurfaces,
  isSurfaceAvailable,
  useApp,
  useIntentResolver
};
//# sourceMappingURL=chunk-6XKO24JP.mjs.map
