@protobase/ui 0.4.1 → 0.5.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/app/app.js CHANGED
@@ -10,6 +10,7 @@ import { useRecord } from "../data/use-record.js";
10
10
  import { recordTitle } from "../live/model-helpers.js";
11
11
  import { humanize } from "../live/naming.js";
12
12
  import { ToastProvider } from "../toasts/toast-provider.js";
13
+ import { useProjectUi } from "./pages/project-ui.js";
13
14
  import { AssistantButton, AssistantPanel } from "./assistant-shell.js";
14
15
  import { breadcrumbFor } from "./breadcrumb-for.js";
15
16
  import { Router, matchRoute, useRouter } from "./router.js";
@@ -18,8 +19,8 @@ import { CreatePage } from "./create/create-page.js";
18
19
  import { ListPage } from "./list-page.js";
19
20
  import { userMenuFromMeta } from "./user-menu-from-meta.js";
20
21
  import { navFromMeta } from "./nav-from-meta.js";
21
- import { ProjectUiProvider, useProjectUi } from "./pages/project-ui.js";
22
22
  import { ComposedPage } from "./pages/composed-page.js";
23
+ import { ProjectUiProvider } from "./pages/project-ui-provider.js";
23
24
  import { RecordPage } from "./record-page.js";
24
25
  import { useGlobalSearch } from "./search/use-global-search.js";
25
26
  import { RuntimeUpdateButton } from "./runtime/runtime-update.js";
@@ -25,7 +25,8 @@ const ConnectedDock = ({ client, page, onClose }) => {
25
25
  error,
26
26
  onSend: (text) => send(text, page),
27
27
  onAction: act,
28
- onClose
28
+ onClose,
29
+ client
29
30
  });
30
31
  };
31
32
  const OwnDock = ({ url, ...props }) => /* @__PURE__ */ jsx(ConnectedDock, {
@@ -0,0 +1,9 @@
1
+ import { ProjectUi } from "./project-ui.js";
2
+ //#region src/app/pages/merge-ui.d.ts
3
+ /**
4
+ * One UI config from several, each after the ones it `extends`, the last being the app's own: components and action
5
+ * handlers are all kept, and one name defined by two configs is an error naming both; every shell slot is drawn,
6
+ * the extended configs' first.
7
+ */
8
+ export declare const mergeUi: (...uis: ProjectUi[]) => ProjectUi;
9
+ //#endregion
@@ -0,0 +1,45 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ //#region src/app/pages/merge-ui.tsx
3
+ const flatten = (ui, label) => [...(ui.extends ?? []).flatMap((parent, index) => flatten(parent, parent.name ?? `${label} extends[${index}]`)), {
4
+ ui,
5
+ label
6
+ }];
7
+ const union = (kind, named, pick) => {
8
+ const merged = {};
9
+ const owners = /* @__PURE__ */ new Map();
10
+ for (const { ui, label } of named) for (const [name, value] of Object.entries(pick(ui) ?? {})) {
11
+ if (Object.hasOwn(merged, name) && merged[name] !== value) throw new Error(`${kind} "${name}" is defined twice, by ${owners.get(name)} and by ${label}; rename one of them`);
12
+ merged[name] = value;
13
+ owners.set(name, owners.get(name) ?? label);
14
+ }
15
+ return merged;
16
+ };
17
+ const slot = (slots) => {
18
+ if (slots.length <= 1) return slots[0];
19
+ const All = () => slots.map((Slot, index) => /* @__PURE__ */ jsx(Slot, {}, index));
20
+ return All;
21
+ };
22
+ /**
23
+ * One UI config from several, each after the ones it `extends`, the last being the app's own: components and action
24
+ * handlers are all kept, and one name defined by two configs is an error naming both; every shell slot is drawn,
25
+ * the extended configs' first.
26
+ */
27
+ const mergeUi = (...uis) => {
28
+ const named = uis.flatMap((ui, index) => flatten(ui, ui.name ?? `UI config ${index + 1}`));
29
+ const components = union("The component", named, (ui) => ui.components);
30
+ const actions = union("The action handler", named, (ui) => ui.actions);
31
+ const actionSlot = slot(named.flatMap(({ ui }) => ui.shell?.actions ? [ui.shell.actions] : []));
32
+ const rightPanel = slot(named.flatMap(({ ui }) => ui.shell?.rightPanel ? [ui.shell.rightPanel] : []));
33
+ const name = uis.at(-1)?.name;
34
+ return {
35
+ ...name && { name },
36
+ components,
37
+ actions,
38
+ ...(actionSlot || rightPanel) && { shell: {
39
+ ...actionSlot && { actions: actionSlot },
40
+ ...rightPanel && { rightPanel }
41
+ } }
42
+ };
43
+ };
44
+ //#endregion
45
+ export { mergeUi };
@@ -0,0 +1,15 @@
1
+ import { ProjectUiContext } from "./project-ui.js";
2
+ import { mergeUi } from "./merge-ui.js";
3
+ import { useMemo } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/app/pages/project-ui-provider.tsx
6
+ /** Provides `ui` merged with the UI configs it extends. */
7
+ const ProjectUiProvider = ({ ui, children }) => {
8
+ const merged = useMemo(() => ui ? mergeUi(ui) : {}, [ui]);
9
+ return /* @__PURE__ */ jsx(ProjectUiContext.Provider, {
10
+ value: merged,
11
+ children
12
+ });
13
+ };
14
+ //#endregion
15
+ export { ProjectUiProvider };
@@ -1,4 +1,4 @@
1
- import { ComponentType, ReactNode } from "react";
1
+ import { ComponentType } from "react";
2
2
  import { Client } from "@protobase/client";
3
3
  //#region src/app/pages/project-ui.d.ts
4
4
  /** What a named action's handler gets: the record it runs on (inside a RecordCard or CardRow), the API and the router. */
@@ -14,9 +14,12 @@ export type ActionContext = {
14
14
  export type ActionHandler = (context: ActionContext) => void | Promise<void>;
15
15
  /**
16
16
  * The project's own React code for composed pages: custom components by the name their layout declares
17
- * (`component('UsageChart')`), handlers for named actions that have no built-in behaviour, and the shell's slots.
17
+ * (`component('UsageChart')`) or a widget part names, handlers for named actions that have no built-in behaviour, and
18
+ * the shell's slots. `extends` lists UI configs merged in before it (see `mergeUi`); `name` names it in their errors.
18
19
  */
19
20
  export type ProjectUi = {
21
+ name?: string;
22
+ extends?: ProjectUi[];
20
23
  components?: Record<string, ComponentType<any>>;
21
24
  actions?: Record<string, ActionHandler>;
22
25
  /** Rendered in the shell on every page: `actions` at the end of the top bar, `rightPanel` beside the page. */
@@ -25,6 +28,6 @@ export type ProjectUi = {
25
28
  rightPanel?: ComponentType;
26
29
  };
27
30
  };
28
- /** Typed identity, for `export default defineUi({ components, actions })` in `protobase.ui.tsx`. */
31
+ /** Typed identity, for `export default defineUi({ extends, components, actions })` in `protobase.ui.tsx`. */
29
32
  export declare const defineUi: (ui: ProjectUi) => ProjectUi;
30
33
  //#endregion
@@ -1,13 +1,9 @@
1
1
  import { createContext, useContext } from "react";
2
- import { jsx } from "react/jsx-runtime";
3
2
  //#region src/app/pages/project-ui.tsx
4
- /** Typed identity, for `export default defineUi({ components, actions })` in `protobase.ui.tsx`. */
3
+ /** Typed identity, for `export default defineUi({ extends, components, actions })` in `protobase.ui.tsx`. */
5
4
  const defineUi = (ui) => ui;
5
+ /** The project's UI, merged with the UI configs it extends; `ProjectUiProvider` provides it. */
6
6
  const ProjectUiContext = createContext({});
7
- const ProjectUiProvider = ({ ui, children }) => /* @__PURE__ */ jsx(ProjectUiContext.Provider, {
8
- value: ui ?? {},
9
- children
10
- });
11
7
  const useProjectUi = () => useContext(ProjectUiContext);
12
8
  //#endregion
13
- export { ProjectUiProvider, defineUi, useProjectUi };
9
+ export { ProjectUiContext, defineUi, useProjectUi };
@@ -1,10 +1,10 @@
1
1
  import { useClient } from "../../data/api-provider.js";
2
2
  import { useToast } from "../../toasts/toast-provider.js";
3
+ import { useProjectUi } from "./project-ui.js";
3
4
  import { useRouter } from "../router.js";
4
5
  import { useAdminMeta, usePermissions } from "../meta-gate.js";
5
6
  import { can } from "../permissions.js";
6
7
  import { fillHref, isAppPath } from "./action-href.js";
7
- import { useProjectUi } from "./project-ui.js";
8
8
  import { useRefreshResource } from "./use-layout-data.js";
9
9
  import { useState } from "react";
10
10
  //#region src/app/pages/use-run-action.ts
@@ -1,3 +1,4 @@
1
+ import { AssistantClient } from "@protobase/client";
1
2
  import { AssistantState } from "@protobase/schema";
2
3
  //#region src/assistant-dock/assistant-dock.d.ts
3
4
  export type AssistantDockProps = {
@@ -9,7 +10,12 @@ export type AssistantDockProps = {
9
10
  onSend?: (text: string) => void;
10
11
  onAction?: (partId: string, actionId: string) => void;
11
12
  onClose?: () => void;
13
+ /** The backend's client, which widgets read and post through with `useBackendData`. */
14
+ client?: AssistantClient;
12
15
  };
13
- /** The assistant beside the app: renders a backend's chat, its cards and status line, and sends messages and clicks back. */
14
- export declare const AssistantDock: ({ state, offline, error, onSend, onAction, onClose }: AssistantDockProps) => import("react").JSX.Element;
16
+ /**
17
+ * The assistant beside the app: renders a backend's chat, its cards, the app's widgets and status line, and sends
18
+ * messages and clicks back.
19
+ */
20
+ export declare const AssistantDock: ({ state, offline, error, onSend, onAction, onClose, client }: AssistantDockProps) => import("react").JSX.Element;
15
21
  //#endregion
@@ -3,13 +3,17 @@ import { ChatThread } from "../chat/chat-thread.js";
3
3
  import { Composer } from "../chat/composer.js";
4
4
  import { SidePanel } from "../side-panel/side-panel.js";
5
5
  import { PartView } from "./part-view.js";
6
+ import { AssistantBackendContext } from "./use-backend-data.js";
6
7
  import { useEffect, useRef } from "react";
7
8
  import { Sparkles } from "lucide-react";
8
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
10
  //#region src/assistant-dock/assistant-dock.tsx
10
11
  const defaultPlaceholder = "Ask a question...";
11
- /** The assistant beside the app: renders a backend's chat, its cards and status line, and sends messages and clicks back. */
12
- const AssistantDock = ({ state, offline, error, onSend, onAction, onClose }) => {
12
+ /**
13
+ * The assistant beside the app: renders a backend's chat, its cards, the app's widgets and status line, and sends
14
+ * messages and clicks back.
15
+ */
16
+ const AssistantDock = ({ state, offline, error, onSend, onAction, onClose, client }) => {
13
17
  const input = useRef(null);
14
18
  useEffect(() => {
15
19
  if (state.placeholder) input.current?.focus();
@@ -29,16 +33,19 @@ const AssistantDock = ({ state, offline, error, onSend, onAction, onClose }) =>
29
33
  busy: state.replying,
30
34
  placeholder: state.placeholder ?? defaultPlaceholder
31
35
  })] }),
32
- children: /* @__PURE__ */ jsx(ChatThread, {
33
- length: state.messages.reduce((sum, message) => sum + message.parts.length, 0),
34
- empty: "Ask a question about this app or its data.",
35
- children: state.messages.map((message) => /* @__PURE__ */ jsx(ChatMessage, {
36
- from: message.from,
37
- children: message.parts.map((part) => /* @__PURE__ */ jsx(PartView, {
38
- part,
39
- onAction
40
- }, part.id))
41
- }, message.id))
36
+ children: /* @__PURE__ */ jsx(AssistantBackendContext.Provider, {
37
+ value: client,
38
+ children: /* @__PURE__ */ jsx(ChatThread, {
39
+ length: state.messages.reduce((sum, message) => sum + message.parts.length, 0),
40
+ empty: "Ask a question about this app or its data.",
41
+ children: state.messages.map((message) => /* @__PURE__ */ jsx(ChatMessage, {
42
+ from: message.from,
43
+ children: message.parts.map((part) => /* @__PURE__ */ jsx(PartView, {
44
+ part,
45
+ onAction
46
+ }, part.id))
47
+ }, message.id))
48
+ })
42
49
  })
43
50
  });
44
51
  };
@@ -2,4 +2,6 @@ import { AssistantDock, AssistantDockProps } from "./assistant-dock.js";
2
2
  import { PartView } from "./part-view.js";
3
3
  import { useAssistant } from "./use-assistant.js";
4
4
  import { useAssistantClient } from "./use-assistant-client.js";
5
- export { AssistantDock, type AssistantDockProps, PartView, useAssistant, useAssistantClient };
5
+ import { BackendData, BackendRefresh, useBackendData } from "./use-backend-data.js";
6
+ import "./widget-view.js";
7
+ export { AssistantDock, type AssistantDockProps, type BackendData, type BackendRefresh, PartView, useAssistant, useAssistantClient, useBackendData };
@@ -1,6 +1,6 @@
1
1
  import { AssistantPart } from "@protobase/schema";
2
2
  //#region src/assistant-dock/part-view.d.ts
3
- /** One part of a message, drawn with the primitive it names. */
3
+ /** One part of a message, drawn with the primitive it names, or a widget with the app's component it names. */
4
4
  export declare const PartView: ({ part, onAction }: {
5
5
  part: AssistantPart;
6
6
  onAction?: (partId: string, actionId: string) => void;
@@ -3,9 +3,10 @@ import { FieldList } from "../action-card/field-list.js";
3
3
  import { CompactTable } from "../compact-table/compact-table.js";
4
4
  import { DiffView } from "../diff-view/diff-view.js";
5
5
  import { StepList } from "../step-list/step-list.js";
6
+ import { WidgetView } from "./widget-view.js";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  //#region src/assistant-dock/part-view.tsx
8
- /** One part of a message, drawn with the primitive it names. */
9
+ /** One part of a message, drawn with the primitive it names, or a widget with the app's component it names. */
9
10
  const PartView = ({ part, onAction }) => {
10
11
  switch (part.type) {
11
12
  case "text": return /* @__PURE__ */ jsx("p", { children: part.text });
@@ -41,6 +42,7 @@ const PartView = ({ part, onAction }) => {
41
42
  }, `${index}-${diff.path}`))
42
43
  ]
43
44
  });
45
+ case "widget": return /* @__PURE__ */ jsx(WidgetView, { part });
44
46
  }
45
47
  };
46
48
  //#endregion
@@ -0,0 +1,23 @@
1
+ import { AssistantClient } from "@protobase/client";
2
+ //#region src/assistant-dock/use-backend-data.d.ts
3
+ /** How long to wait before reading again: a number of milliseconds while the data can still change, `false` once it cannot. */
4
+ export type BackendRefresh<T> = number | false | ((data: T | undefined) => number | false);
5
+ export type BackendData<T> = {
6
+ /** The last answer read or posted; `undefined` until the first one. */
7
+ data?: T;
8
+ /** Why the last read or post failed, such as an `ApiError` with the backend's detail; cleared by the next success. */
9
+ error?: Error;
10
+ /** Reads again now. */
11
+ reload: () => void;
12
+ /** Posts `body` as JSON to `path` on the backend; its answer replaces `data`. */
13
+ post: (path: string, body?: unknown) => Promise<void>;
14
+ };
15
+ /**
16
+ * JSON from the assistant backend at `path` (see `AssistantClient.fetch`), read as the signed-in user each time the
17
+ * widget mounts, so a widget in an old chat shows the data as it is now; with `refreshMs`, read again while it can
18
+ * still change. Only inside the dock, which provides the backend.
19
+ */
20
+ export declare const useBackendData: <T = unknown>(path: string, { refreshMs }?: {
21
+ refreshMs?: BackendRefresh<T>;
22
+ }) => BackendData<T>;
23
+ //#endregion
@@ -0,0 +1,67 @@
1
+ import { createContext, useContext, useEffect, useRef, useState } from "react";
2
+ //#region src/assistant-dock/use-backend-data.ts
3
+ /** The assistant backend the dock talks to, for the widgets it draws; `AssistantDock` provides it from its `client`. */
4
+ const AssistantBackendContext = createContext(void 0);
5
+ const asError = (reason) => reason instanceof Error ? reason : /* @__PURE__ */ new Error("The assistant backend did not answer");
6
+ const refreshOf = (refresh, data) => typeof refresh === "function" ? refresh(data) : refresh;
7
+ /**
8
+ * JSON from the assistant backend at `path` (see `AssistantClient.fetch`), read as the signed-in user each time the
9
+ * widget mounts, so a widget in an old chat shows the data as it is now; with `refreshMs`, read again while it can
10
+ * still change. Only inside the dock, which provides the backend.
11
+ */
12
+ const useBackendData = (path, { refreshMs = false } = {}) => {
13
+ const client = useContext(AssistantBackendContext);
14
+ const [state, setState] = useState({});
15
+ const [reads, setReads] = useState(0);
16
+ const refresh = useRef(refreshMs);
17
+ refresh.current = refreshMs;
18
+ const latest = useRef(void 0);
19
+ useEffect(() => {
20
+ if (!client) {
21
+ setState({ error: /* @__PURE__ */ new Error("useBackendData works only in a widget the assistant dock draws") });
22
+ return;
23
+ }
24
+ let active = true;
25
+ let timer;
26
+ const read = async () => {
27
+ const answer = await client.fetch(path).then((response) => response.json()).then((data) => ({ data }), (reason) => ({
28
+ data: latest.current,
29
+ error: asError(reason)
30
+ }));
31
+ if (!active) return;
32
+ latest.current = answer.data;
33
+ setState(answer);
34
+ const wait = refreshOf(refresh.current, answer.data);
35
+ if (wait !== false) timer = setTimeout(read, wait);
36
+ };
37
+ read();
38
+ return () => {
39
+ active = false;
40
+ clearTimeout(timer);
41
+ };
42
+ }, [
43
+ client,
44
+ path,
45
+ reads
46
+ ]);
47
+ return {
48
+ ...state,
49
+ reload: () => setReads((count) => count + 1),
50
+ post: async (to, body) => {
51
+ if (!client) throw new Error("useBackendData works only in a widget the assistant dock draws");
52
+ const init = {
53
+ method: "POST",
54
+ headers: { "content-type": "application/json" },
55
+ body: JSON.stringify(body ?? {})
56
+ };
57
+ const answer = await client.fetch(to, init).then((response) => response.json()).then((data) => ({ data }), (reason) => ({
58
+ data: latest.current,
59
+ error: asError(reason)
60
+ }));
61
+ latest.current = answer.data;
62
+ setState(answer);
63
+ }
64
+ };
65
+ };
66
+ //#endregion
67
+ export { AssistantBackendContext, useBackendData };
@@ -0,0 +1 @@
1
+ import "@protobase/schema";
@@ -0,0 +1,44 @@
1
+ import { ActionCard } from "../action-card/action-card.js";
2
+ import { useProjectUi } from "../app/pages/project-ui.js";
3
+ import { Component } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ //#region src/assistant-dock/widget-view.tsx
6
+ /** The part's fallback as a plain card with `note`, or a muted line naming the component when it has none. */
7
+ const Fallback = ({ part, note }) => part.fallback ? /* @__PURE__ */ jsx(ActionCard, {
8
+ title: part.fallback.title,
9
+ note,
10
+ children: part.fallback.body && /* @__PURE__ */ jsx("p", {
11
+ className: "text-[13px]",
12
+ children: part.fallback.body
13
+ })
14
+ }) : /* @__PURE__ */ jsx("p", {
15
+ className: "text-xs text-muted-foreground",
16
+ children: `${part.name}: ${note}`
17
+ });
18
+ var WidgetBoundary = class extends Component {
19
+ state = { failed: false };
20
+ static getDerivedStateFromError() {
21
+ return { failed: true };
22
+ }
23
+ render() {
24
+ return this.state.failed ? /* @__PURE__ */ jsx(Fallback, {
25
+ part: this.props.part,
26
+ note: "It could not be drawn."
27
+ }) : this.props.children;
28
+ }
29
+ };
30
+ /** A widget part, drawn by the app's component of its name with its props; the fallback when the app has none. */
31
+ const WidgetView = ({ part }) => {
32
+ const { components = {} } = useProjectUi();
33
+ const Widget = Object.hasOwn(components, part.name) ? components[part.name] : void 0;
34
+ if (!Widget) return /* @__PURE__ */ jsx(Fallback, {
35
+ part,
36
+ note: "This version of the app cannot show it live."
37
+ });
38
+ return /* @__PURE__ */ jsx(WidgetBoundary, {
39
+ part,
40
+ children: /* @__PURE__ */ jsx(Widget, { ...part.props })
41
+ });
42
+ };
43
+ //#endregion
44
+ export { WidgetView };
package/dist/index.d.ts CHANGED
@@ -8,12 +8,14 @@ import { useList } from "./data/use-list.js";
8
8
  import { useRecord } from "./data/use-record.js";
9
9
  import { useFacets, useHistogram, useSeries } from "./data/use-aggregates.js";
10
10
  import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
11
+ import { mergeUi } from "./app/pages/merge-ui.js";
11
12
  import { LayoutRecord, useLayoutRecord } from "./app/pages/record-scope.js";
12
13
  import { useApiToken } from "./auth/use-api-token.js";
13
14
  import { AssistantDock, AssistantDockProps } from "./assistant-dock/assistant-dock.js";
14
15
  import { PartView } from "./assistant-dock/part-view.js";
15
16
  import { useAssistant } from "./assistant-dock/use-assistant.js";
16
17
  import { useAssistantClient } from "./assistant-dock/use-assistant-client.js";
18
+ import { BackendData, BackendRefresh, useBackendData } from "./assistant-dock/use-backend-data.js";
17
19
  import "./assistant-dock/index.js";
18
20
  import { ChatMessage } from "./chat/chat-message.js";
19
21
  import { ChatThread, ChatThreadProps } from "./chat/chat-thread.js";
@@ -28,4 +30,4 @@ import { Step, StepList, StepState } from "./step-list/step-list.js";
28
30
  import { DiffView, DiffViewProps } from "./diff-view/diff-view.js";
29
31
  import { DiffLine, diffStats, parseDiff } from "./diff-view/diff.js";
30
32
  import { CompactTable, CompactTableProps } from "./compact-table/compact-table.js";
31
- export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ApiProvider, type ApiProviderProps, App, type AppProps, AssistantDock, type AssistantDockProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type DiffLine, DiffView, type DiffViewProps, FieldList, type LayoutRecord, type MetaData, PartView, type ProjectUi, SidePanel, type SidePanelProps, type Step, StepList, type StepState, defineUi, diffStats, isConflict, parseDiff, useApiToken, useAssistant, useAssistantClient, useClient, useFacets, useHistogram, useLayoutRecord, useList, useMeta, useRecord, useSeries, useUpdateRecord };
33
+ export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ApiProvider, type ApiProviderProps, App, type AppProps, AssistantDock, type AssistantDockProps, type BackendData, type BackendRefresh, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type DiffLine, DiffView, type DiffViewProps, FieldList, type LayoutRecord, type MetaData, PartView, type ProjectUi, SidePanel, type SidePanelProps, type Step, StepList, type StepState, defineUi, diffStats, isConflict, mergeUi, parseDiff, useApiToken, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useHistogram, useLayoutRecord, useList, useMeta, useRecord, useSeries, useUpdateRecord };
package/dist/index.js CHANGED
@@ -11,15 +11,17 @@ import { CompactTable } from "./compact-table/compact-table.js";
11
11
  import { diffStats, parseDiff } from "./diff-view/diff.js";
12
12
  import { DiffView } from "./diff-view/diff-view.js";
13
13
  import { StepList } from "./step-list/step-list.js";
14
+ import { defineUi } from "./app/pages/project-ui.js";
14
15
  import { PartView } from "./assistant-dock/part-view.js";
16
+ import { useBackendData } from "./assistant-dock/use-backend-data.js";
15
17
  import { AssistantDock } from "./assistant-dock/assistant-dock.js";
16
18
  import { useAssistant } from "./assistant-dock/use-assistant.js";
17
19
  import { useAssistantClient } from "./assistant-dock/use-assistant-client.js";
18
20
  import { useMeta } from "./data/use-meta.js";
19
21
  import { useFacets, useHistogram, useSeries } from "./data/use-aggregates.js";
20
22
  import { useLayoutRecord } from "./app/pages/record-scope.js";
21
- import { defineUi } from "./app/pages/project-ui.js";
23
+ import { mergeUi } from "./app/pages/merge-ui.js";
22
24
  import { useList } from "./data/use-list.js";
23
25
  import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
24
26
  import { App } from "./app/app.js";
25
- export { ActionCard, ApiProvider, App, AssistantDock, ChatMessage, ChatThread, CompactTable, Composer, DiffView, FieldList, PartView, SidePanel, StepList, defineUi, diffStats, isConflict, parseDiff, useApiToken, useAssistant, useAssistantClient, useClient, useFacets, useHistogram, useLayoutRecord, useList, useMeta, useRecord, useSeries, useUpdateRecord };
27
+ export { ActionCard, ApiProvider, App, AssistantDock, ChatMessage, ChatThread, CompactTable, Composer, DiffView, FieldList, PartView, SidePanel, StepList, defineUi, diffStats, isConflict, mergeUi, parseDiff, useApiToken, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useHistogram, useLayoutRecord, useList, useMeta, useRecord, useSeries, useUpdateRecord };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@protobase/ui",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "The Protobase admin app and the React hooks and components for custom UI.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -34,9 +34,9 @@
34
34
  "dependencies": {
35
35
  "@dnd-kit/core": "^6.3.1",
36
36
  "@dnd-kit/sortable": "^10.0.0",
37
- "@protobase/client": "0.4.1",
38
- "@protobase/layout": "0.4.1",
39
- "@protobase/schema": "0.4.1",
37
+ "@protobase/client": "0.5.0",
38
+ "@protobase/layout": "0.5.0",
39
+ "@protobase/schema": "0.5.0",
40
40
  "@tanstack/react-query": "^5.104.1",
41
41
  "@tanstack/react-table": "^9.2.6",
42
42
  "@tanstack/react-virtual": "^3.14.13",