@palettelab/sdk 0.1.15 → 0.1.16

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/index.mjs CHANGED
@@ -171,6 +171,7 @@ function createMockPlatformContext(overrides = {}) {
171
171
  apiFetch: async () => new Response(JSON.stringify({}), { status: 200 }),
172
172
  navigate: () => {
173
173
  },
174
+ routePath: "/",
174
175
  showToast: () => {
175
176
  },
176
177
  ...overrides
@@ -660,14 +661,216 @@ function usePluginTranslations(resources, options = {}) {
660
661
  };
661
662
  }
662
663
 
664
+ // src/router.tsx
665
+ import {
666
+ Component,
667
+ createContext as createContext2,
668
+ createElement as createElement2,
669
+ useCallback as useCallback2,
670
+ useContext as useContext2,
671
+ useEffect,
672
+ useMemo,
673
+ useState
674
+ } from "react";
675
+ var RouterCtx = createContext2(null);
676
+ var NOT_FOUND = /* @__PURE__ */ Symbol.for("palette.router.not-found");
677
+ function notFound() {
678
+ throw Object.assign(new Error("Palette route not found"), { code: NOT_FOUND });
679
+ }
680
+ function normalizePath(path) {
681
+ const clean = String(path || "/").split("#")[0].split("?")[0] || "/";
682
+ return `/${clean.replace(/^\/+/, "").replace(/\/+$/, "")}`.replace(/^\/$/, "/");
683
+ }
684
+ function splitPath(path) {
685
+ return normalizePath(path).split("/").filter(Boolean).map(decodeURIComponent);
686
+ }
687
+ function routeScore(route) {
688
+ if (typeof route.score === "number") return route.score;
689
+ return route.segments.reduce((score, segment) => {
690
+ if (segment.kind === "static") return score + 10;
691
+ if (segment.kind === "dynamic") return score + 5;
692
+ return score + (segment.optional ? 1 : 2);
693
+ }, route.segments.length);
694
+ }
695
+ function matchRoute(route, path) {
696
+ const parts = splitPath(path);
697
+ const params = {};
698
+ let index = 0;
699
+ for (const segment of route.segments) {
700
+ if (segment.kind === "catchAll") {
701
+ const rest = parts.slice(index);
702
+ if (!segment.optional && rest.length === 0) return null;
703
+ params[segment.name] = rest;
704
+ index = parts.length;
705
+ break;
706
+ }
707
+ const value = parts[index];
708
+ if (value === void 0) return null;
709
+ if (segment.kind === "static") {
710
+ if (value !== segment.value) return null;
711
+ } else {
712
+ params[segment.name] = value;
713
+ }
714
+ index += 1;
715
+ }
716
+ return index === parts.length ? params : null;
717
+ }
718
+ function currentPluginPath(pluginId, routePath) {
719
+ if (routePath) return normalizePath(routePath);
720
+ if (typeof window === "undefined") return "/";
721
+ const path = window.location.pathname;
722
+ if (pluginId) {
723
+ const prefix = `/apps/${pluginId}`;
724
+ if (path === prefix) return "/";
725
+ if (path.startsWith(`${prefix}/`)) return normalizePath(path.slice(prefix.length));
726
+ }
727
+ return normalizePath(path);
728
+ }
729
+ function currentSearchParams() {
730
+ if (typeof window === "undefined") return new URLSearchParams();
731
+ return new URLSearchParams(window.location.search);
732
+ }
733
+ var PaletteRouteErrorBoundary = class extends Component {
734
+ constructor() {
735
+ super(...arguments);
736
+ this.state = { error: null };
737
+ this.reset = () => this.setState({ error: null });
738
+ }
739
+ static getDerivedStateFromError(error) {
740
+ return { error };
741
+ }
742
+ componentDidCatch(_error, _info) {
743
+ }
744
+ render() {
745
+ const error = this.state.error;
746
+ if (!error) return this.props.children;
747
+ if (error.code === NOT_FOUND && this.props.notFound) {
748
+ return createElement2(this.props.notFound);
749
+ }
750
+ if (this.props.fallback) {
751
+ return createElement2(this.props.fallback, { error, reset: this.reset });
752
+ }
753
+ throw error;
754
+ }
755
+ };
756
+ function renderRoute(route) {
757
+ const page = createElement2(route.page);
758
+ const wrapped = (route.layouts || []).reduceRight(
759
+ (children, Layout) => createElement2(Layout, null, children),
760
+ page
761
+ );
762
+ return createElement2(
763
+ PaletteRouteErrorBoundary,
764
+ { fallback: route.error, notFound: route.notFound },
765
+ wrapped
766
+ );
767
+ }
768
+ function PaletteAppRouter({
769
+ routes,
770
+ notFound: NotFound
771
+ }) {
772
+ const platform = usePlatform();
773
+ const [location, setLocation] = useState(() => ({
774
+ pathname: currentPluginPath(platform.pluginId, platform.routePath),
775
+ searchParams: currentSearchParams()
776
+ }));
777
+ useEffect(() => {
778
+ setLocation({
779
+ pathname: currentPluginPath(platform.pluginId, platform.routePath),
780
+ searchParams: currentSearchParams()
781
+ });
782
+ }, [platform.pluginId, platform.routePath]);
783
+ useEffect(() => {
784
+ const sync = () => setLocation({
785
+ pathname: currentPluginPath(platform.pluginId, platform.routePath),
786
+ searchParams: currentSearchParams()
787
+ });
788
+ window.addEventListener("popstate", sync);
789
+ return () => window.removeEventListener("popstate", sync);
790
+ }, [platform.pluginId, platform.routePath]);
791
+ const sortedRoutes = useMemo(
792
+ () => [...routes].sort((a, b) => routeScore(b) - routeScore(a)),
793
+ [routes]
794
+ );
795
+ const matched = useMemo(() => {
796
+ for (const route of sortedRoutes) {
797
+ const params = matchRoute(route, location.pathname);
798
+ if (params) return { route, params };
799
+ }
800
+ return null;
801
+ }, [location.pathname, sortedRoutes]);
802
+ const navigate = useCallback2((target, replace = false) => {
803
+ const [pathPart, queryPart = ""] = String(target || "/").split("?");
804
+ const pathname = normalizePath(pathPart);
805
+ const next = `${pathname}${queryPart ? `?${queryPart}` : ""}`;
806
+ setLocation({ pathname, searchParams: new URLSearchParams(queryPart) });
807
+ const currentPath = typeof window === "undefined" ? "" : window.location.pathname;
808
+ const inPalettePath = platform.pluginId && (currentPath.startsWith(`/apps/${platform.pluginId}`) || platform.routePath !== void 0);
809
+ const osPath = inPalettePath && platform.pluginId ? `/apps/${platform.pluginId}${pathname === "/" ? "" : pathname}${queryPart ? `?${queryPart}` : ""}` : next;
810
+ if (replace && typeof window !== "undefined") window.history.replaceState(null, "", osPath);
811
+ else platform.navigate(osPath);
812
+ }, [platform]);
813
+ const state = useMemo(() => ({
814
+ pathname: location.pathname,
815
+ searchParams: location.searchParams,
816
+ params: matched?.params || {},
817
+ push: (path) => navigate(path, false),
818
+ replace: (path) => navigate(path, true)
819
+ }), [location.pathname, location.searchParams, matched?.params, navigate]);
820
+ const content = matched ? renderRoute(matched.route) : NotFound ? createElement2(NotFound) : createElement2("div", null, "Page not found");
821
+ return createElement2(RouterCtx.Provider, { value: state }, content);
822
+ }
823
+ function useRouterState() {
824
+ const value = useContext2(RouterCtx);
825
+ if (!value) throw new Error("Palette app router hooks must be used inside PaletteAppRouter");
826
+ return value;
827
+ }
828
+ function useRouter() {
829
+ const { push, replace } = useRouterState();
830
+ return { push, replace, back: () => window.history.back(), forward: () => window.history.forward() };
831
+ }
832
+ function usePathname() {
833
+ return useRouterState().pathname;
834
+ }
835
+ function useSearchParams() {
836
+ return useRouterState().searchParams;
837
+ }
838
+ function useParams() {
839
+ return useRouterState().params;
840
+ }
841
+ function Link({
842
+ href,
843
+ replace,
844
+ onClick,
845
+ children,
846
+ ...props
847
+ }) {
848
+ const router = useRouter();
849
+ return createElement2(
850
+ "a",
851
+ {
852
+ ...props,
853
+ href,
854
+ onClick: (event) => {
855
+ onClick?.(event);
856
+ if (event.defaultPrevented || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
857
+ event.preventDefault();
858
+ if (replace) router.replace(href);
859
+ else router.push(href);
860
+ }
861
+ },
862
+ children
863
+ );
864
+ }
865
+
663
866
  // src/hooks/use-plugin-tasks.ts
664
- import { useCallback as useCallback2, useEffect, useState } from "react";
867
+ import { useCallback as useCallback3, useEffect as useEffect2, useState as useState2 } from "react";
665
868
  function usePluginTasks(agentId) {
666
869
  const { apiFetch: apiFetch2 } = usePlatform();
667
- const [tasks, setTasks] = useState([]);
668
- const [stats, setStats] = useState(null);
669
- const [loading, setLoading] = useState(true);
670
- const fetchTasks = useCallback2(async () => {
870
+ const [tasks, setTasks] = useState2([]);
871
+ const [stats, setStats] = useState2(null);
872
+ const [loading, setLoading] = useState2(true);
873
+ const fetchTasks = useCallback3(async () => {
671
874
  try {
672
875
  const params = new URLSearchParams();
673
876
  if (agentId) params.set("agent_id", String(agentId));
@@ -676,18 +879,18 @@ function usePluginTasks(agentId) {
676
879
  } catch {
677
880
  }
678
881
  }, [apiFetch2, agentId]);
679
- const fetchStats = useCallback2(async () => {
882
+ const fetchStats = useCallback3(async () => {
680
883
  try {
681
884
  const res = await apiFetch2("/api/v1/tasks/stats");
682
885
  setStats(await res.json());
683
886
  } catch {
684
887
  }
685
888
  }, [apiFetch2]);
686
- useEffect(() => {
889
+ useEffect2(() => {
687
890
  setLoading(true);
688
891
  Promise.all([fetchTasks(), fetchStats()]).finally(() => setLoading(false));
689
892
  }, [fetchTasks, fetchStats]);
690
- const createTask = useCallback2(async (payload) => {
893
+ const createTask = useCallback3(async (payload) => {
691
894
  const res = await apiFetch2("/api/v1/tasks", {
692
895
  method: "POST",
693
896
  body: JSON.stringify(payload)
@@ -696,7 +899,7 @@ function usePluginTasks(agentId) {
696
899
  await Promise.all([fetchTasks(), fetchStats()]);
697
900
  return task;
698
901
  }, [apiFetch2, fetchTasks, fetchStats]);
699
- const updateTask = useCallback2(async (taskId, payload) => {
902
+ const updateTask = useCallback3(async (taskId, payload) => {
700
903
  const res = await apiFetch2(`/api/v1/tasks/${taskId}`, {
701
904
  method: "PATCH",
702
905
  body: JSON.stringify(payload)
@@ -705,7 +908,7 @@ function usePluginTasks(agentId) {
705
908
  await Promise.all([fetchTasks(), fetchStats()]);
706
909
  return task;
707
910
  }, [apiFetch2, fetchTasks, fetchStats]);
708
- const deleteTask = useCallback2(async (taskId) => {
911
+ const deleteTask = useCallback3(async (taskId) => {
709
912
  await apiFetch2(`/api/v1/tasks/${taskId}`, { method: "DELETE" });
710
913
  await Promise.all([fetchTasks(), fetchStats()]);
711
914
  }, [apiFetch2, fetchTasks, fetchStats]);
@@ -713,23 +916,23 @@ function usePluginTasks(agentId) {
713
916
  }
714
917
 
715
918
  // src/hooks/use-plugin-data-rooms.ts
716
- import { useCallback as useCallback3, useEffect as useEffect2, useState as useState2 } from "react";
919
+ import { useCallback as useCallback4, useEffect as useEffect3, useState as useState3 } from "react";
717
920
  function usePluginDataRooms() {
718
921
  const { apiFetch: apiFetch2 } = usePlatform();
719
- const [rooms, setRooms] = useState2([]);
720
- const [loading, setLoading] = useState2(true);
721
- const fetchRooms = useCallback3(async () => {
922
+ const [rooms, setRooms] = useState3([]);
923
+ const [loading, setLoading] = useState3(true);
924
+ const fetchRooms = useCallback4(async () => {
722
925
  try {
723
926
  const res = await apiFetch2("/api/v1/data-rooms");
724
927
  setRooms(await res.json());
725
928
  } catch {
726
929
  }
727
930
  }, [apiFetch2]);
728
- useEffect2(() => {
931
+ useEffect3(() => {
729
932
  setLoading(true);
730
933
  fetchRooms().finally(() => setLoading(false));
731
934
  }, [fetchRooms]);
732
- const fetchFolder = useCallback3(async (roomId, folderId) => {
935
+ const fetchFolder = useCallback4(async (roomId, folderId) => {
733
936
  const path = folderId ? `/api/v1/data-rooms/${roomId}/folders/${folderId}` : `/api/v1/data-rooms/${roomId}`;
734
937
  const res = await apiFetch2(path);
735
938
  return res.json();
@@ -738,19 +941,19 @@ function usePluginDataRooms() {
738
941
  }
739
942
 
740
943
  // src/hooks/use-plugin-chat.ts
741
- import { useCallback as useCallback4, useRef, useState as useState3 } from "react";
944
+ import { useCallback as useCallback5, useRef, useState as useState4 } from "react";
742
945
  function usePluginChat(agentId) {
743
946
  const { apiFetch: apiFetch2 } = usePlatform();
744
- const [threads, setThreads] = useState3([]);
745
- const [messages, setMessages] = useState3([]);
746
- const [streaming, setStreaming] = useState3(false);
747
- const [activeThreadId, setActiveThreadId] = useState3(null);
947
+ const [threads, setThreads] = useState4([]);
948
+ const [messages, setMessages] = useState4([]);
949
+ const [streaming, setStreaming] = useState4(false);
950
+ const [activeThreadId, setActiveThreadId] = useState4(null);
748
951
  const abortRef = useRef(null);
749
- const fetchThreads = useCallback4(async () => {
952
+ const fetchThreads = useCallback5(async () => {
750
953
  const res = await apiFetch2(`/api/v1/chat/${agentId}/threads`);
751
954
  setThreads(await res.json());
752
955
  }, [apiFetch2, agentId]);
753
- const createThread = useCallback4(async () => {
956
+ const createThread = useCallback5(async () => {
754
957
  const res = await apiFetch2(`/api/v1/chat/${agentId}/threads`, { method: "POST" });
755
958
  const thread = await res.json();
756
959
  setActiveThreadId(thread.id);
@@ -758,13 +961,13 @@ function usePluginChat(agentId) {
758
961
  await fetchThreads();
759
962
  return thread;
760
963
  }, [apiFetch2, agentId, fetchThreads]);
761
- const fetchMessages = useCallback4(async (threadId) => {
964
+ const fetchMessages = useCallback5(async (threadId) => {
762
965
  const res = await apiFetch2(`/api/v1/chat/threads/${threadId}/messages`);
763
966
  const msgs = await res.json();
764
967
  setMessages(msgs);
765
968
  setActiveThreadId(threadId);
766
969
  }, [apiFetch2]);
767
- const sendMessage = useCallback4(async (threadId, content) => {
970
+ const sendMessage = useCallback5(async (threadId, content) => {
768
971
  setStreaming(true);
769
972
  abortRef.current = new AbortController();
770
973
  const userMsg = {
@@ -832,7 +1035,7 @@ function usePluginChat(agentId) {
832
1035
  abortRef.current = null;
833
1036
  }
834
1037
  }, []);
835
- const stopStreaming = useCallback4(() => {
1038
+ const stopStreaming = useCallback5(() => {
836
1039
  abortRef.current?.abort();
837
1040
  }, []);
838
1041
  return {
@@ -849,8 +1052,10 @@ function usePluginChat(agentId) {
849
1052
  }
850
1053
  export {
851
1054
  DataRoomClient,
1055
+ Link,
852
1056
  OrganizationClient,
853
1057
  PaletteApiError,
1058
+ PaletteAppRouter,
854
1059
  PlatformCtx,
855
1060
  PluginProvider,
856
1061
  StorageClient,
@@ -870,14 +1075,19 @@ export {
870
1075
  isPaletteApiError,
871
1076
  isSandboxRuntime,
872
1077
  normalizePaletteLanguage,
1078
+ notFound,
873
1079
  setBaseUrl,
874
1080
  translate,
875
1081
  updateInstallConfig,
876
1082
  uploadToSignedUrl,
1083
+ useParams,
1084
+ usePathname,
877
1085
  usePlatform,
878
1086
  usePluginChat,
879
1087
  usePluginDataRooms,
880
1088
  usePluginTasks,
881
1089
  usePluginTranslations,
1090
+ useRouter,
1091
+ useSearchParams,
882
1092
  withPluginProvider
883
1093
  };
@@ -106,7 +106,7 @@ interface PluginManifest {
106
106
  frontend?: {
107
107
  entry: string;
108
108
  sandbox?: boolean;
109
- framework?: "react" | "next";
109
+ framework?: "react" | "next" | "palette-app";
110
110
  config?: string;
111
111
  };
112
112
  backend?: {
@@ -165,6 +165,8 @@ interface PlatformContext {
165
165
  apiFetch: (path: string, init?: RequestInit) => Promise<Response>;
166
166
  /** Navigate to a platform route */
167
167
  navigate: (path: string) => void;
168
+ /** Current route path within the plugin app, when mounted by Palette OS */
169
+ routePath?: string;
168
170
  /** Show a toast notification */
169
171
  showToast: (message: string, type?: "success" | "error" | "info") => void;
170
172
  /** Permissions declared for the current plugin install/runtime */
@@ -106,7 +106,7 @@ interface PluginManifest {
106
106
  frontend?: {
107
107
  entry: string;
108
108
  sandbox?: boolean;
109
- framework?: "react" | "next";
109
+ framework?: "react" | "next" | "palette-app";
110
110
  config?: string;
111
111
  };
112
112
  backend?: {
@@ -165,6 +165,8 @@ interface PlatformContext {
165
165
  apiFetch: (path: string, init?: RequestInit) => Promise<Response>;
166
166
  /** Navigate to a platform route */
167
167
  navigate: (path: string) => void;
168
+ /** Current route path within the plugin app, when mounted by Palette OS */
169
+ routePath?: string;
168
170
  /** Show a toast notification */
169
171
  showToast: (message: string, type?: "success" | "error" | "info") => void;
170
172
  /** Permissions declared for the current plugin install/runtime */