@ethisyscore/plugin-ui 1.136.0 → 1.137.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/README.md +22 -1
- package/dist/platform-react/index.cjs +78 -0
- package/dist/platform-react/index.cjs.map +1 -1
- package/dist/platform-react/index.d.cts +51 -2
- package/dist/platform-react/index.d.ts +51 -2
- package/dist/platform-react/index.js +60 -2
- package/dist/platform-react/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ and the shared MUI component surface externalised to the host's instances.
|
|
|
16
16
|
| Subpath | Key exports | Purpose |
|
|
17
17
|
|---|---|---|
|
|
18
18
|
| `.` | `createPortBridgeClient`, `BridgeClientContext` / `useBridgeClient`, `useBridgeTheme` / `useBridgeLocale`, `ExtensionRuntimeProvider`, `useMcpResource` / `useMcpTool` / `useMcpQuery` / `unwrapItems`, `useHostIdentity` / `useCurrentUser`, `definePlatformReactPage` / `emitNavigation` / `PlatformReactPageProps` | Bridge client + theme/locale context, brokered-MCP data hooks, host identity, and the base PlatformReact page contract |
|
|
19
|
-
| `./platform-react` | `definePlatformReactPluginPage` / `createPluginPageDefiner`, `definePlatformReactPluginOverlay` / `useOverlayHost`, `useView` / `createUseView` / `ViewComponent` / `isComponent`, `BaseMcpService` / `useToolInvoker` / `useToolInvokerMap`, `useAuthenticatedQuery` / `useAuthenticatedQueries`, `injectPluginStyles` / `PluginStyleScope`, `createReactRouterShim`, `usePluginRealtime`, `useBackgroundOperation` / `useOperationsByParent` / `useLatestTerminalOperationByParent` / `hasKind`, `
|
|
19
|
+
| `./platform-react` | `definePlatformReactPluginPage` / `createPluginPageDefiner`, `definePlatformReactPluginOverlay` / `useOverlayHost`, `useView` / `createUseView` / `ViewComponent` / `isComponent`, `BaseMcpService` / `useToolInvoker` / `useToolInvokerMap`, `useAuthenticatedQuery` / `useAuthenticatedQueries`, `injectPluginStyles` / `PluginStyleScope`, `createReactRouterShim`, `usePluginRealtime`, `useBackgroundOperation` / `useOperationsByParent` / `useLatestTerminalOperationByParent` / `hasKind`, `useUploadToStorage` / `PluginStorageUploadError` / `isPluginStorageUploadError` / `validatePluginStoragePrefix`, `useMcpUpload` (legacy CORE storage), `useManagedLifecycle` | The plugin authoring layer: page/overlay wrappers (react-query + manifest + style root), the static `useView` adapter pattern, the MCP service base + tool invokers, pre-authenticated react-query wrappers, scoped-style injection, router shim, realtime, following the plugin's own background operations, and file upload |
|
|
20
20
|
| `./icons` | `NextureIcons`, `IconMap`, 470+ `Ni*` icon components, `sizeHelper` / `strokeSizeHelper`, `IconName` / `IconSize` / `IconVariant` | The gogo-ui "nexture" SVG icon set with props types and size/stroke helpers |
|
|
21
21
|
| `./components/ui` | `Button`, `Card`, `Dialog`, `Table`, `TextField`, `Select`, `Tabs`, `Typography`, `Grid`, `Stack`, `Menu`, `DebouncedSearchInput`, … | MUI component re-exports plus the Dialog scroll-lock wrapper and the debounced search field. These are **host externals** — never bundled; production builds bind them to the host's MUI instance |
|
|
22
22
|
| `./components/layout` | `PageHeader` / `usePageHeader` / `withPageHeader` / `buildBreadcrumbs`, `FillGrid` / `FillGridItem` / `FillGap` / `FillSpan`, sidebar-action context (`SidebarActionProvider`, `useSidebarAction` / `useSidebarActions` / `useTriggerSidebarAction`), `RouteMeta` / `RouteMetaEntry` | Layout primitives (flex-wrap grid), the page-header layer + breadcrumb builder, and the sidebar-action context/provider |
|
|
@@ -52,6 +52,27 @@ service extending `BaseMcpService` and consume it through `useToolInvoker`. See
|
|
|
52
52
|
the top-level README §"services over MCP" and the
|
|
53
53
|
[FE migration playbook](../../docs/app-migration/fe-migration-playbook.md).
|
|
54
54
|
|
|
55
|
+
### Uploading a browser file
|
|
56
|
+
|
|
57
|
+
Browser uploads go through `useUploadToStorage` into the app's own plugin storage. Never base64 a file through a tool call.
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useUploadToStorage, isPluginStorageUploadError } from "@ethisyscore/plugin-ui/platform-react";
|
|
61
|
+
|
|
62
|
+
const { upload, loading, error } = useUploadToStorage();
|
|
63
|
+
|
|
64
|
+
async function onFile(file: File) {
|
|
65
|
+
try {
|
|
66
|
+
const { path } = await upload(file, { pathPrefix: "cvs" });
|
|
67
|
+
await recordCv({ path }); // the app's own command; the backend reads `path` via IPluginStorage
|
|
68
|
+
} catch (err) {
|
|
69
|
+
if (isPluginStorageUploadError(err) && err.reason === "quota_exceeded") { /* ... */ }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The prefix is one or more `/`-separated `[A-Za-z0-9_-]` segments, at most 84 characters, with no leading or trailing `/`. It is refused, never repaired. Files are capped at 30 MB, and only allow-listed passive types (`PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES`) are accepted. See [CAPABILITIES.md](../../CAPABILITIES.md#browser-file-uploads) for the full rules and the error reasons.
|
|
75
|
+
|
|
55
76
|
## Build and test
|
|
56
77
|
|
|
57
78
|
```bash
|
|
@@ -985,6 +985,63 @@ function useMcpUpload(opts) {
|
|
|
985
985
|
);
|
|
986
986
|
return { upload, progress, loading, error };
|
|
987
987
|
}
|
|
988
|
+
function useUploadToStorage(opts) {
|
|
989
|
+
const transport = extensionRuntime.useExtensionRuntimeTransport(opts?.transport);
|
|
990
|
+
const maxSizeBytes = opts?.maxSizeBytes;
|
|
991
|
+
const [progress, setProgress] = react.useState(0);
|
|
992
|
+
const [loading, setLoading] = react.useState(false);
|
|
993
|
+
const [error, setError] = react.useState(void 0);
|
|
994
|
+
const controllerRef = react.useRef(null);
|
|
995
|
+
const mountedRef = react.useRef(true);
|
|
996
|
+
react.useEffect(() => {
|
|
997
|
+
mountedRef.current = true;
|
|
998
|
+
return () => {
|
|
999
|
+
mountedRef.current = false;
|
|
1000
|
+
controllerRef.current?.abort();
|
|
1001
|
+
};
|
|
1002
|
+
}, []);
|
|
1003
|
+
const upload = react.useCallback(
|
|
1004
|
+
async (file, target) => {
|
|
1005
|
+
const controller = new AbortController();
|
|
1006
|
+
controllerRef.current?.abort();
|
|
1007
|
+
controllerRef.current = controller;
|
|
1008
|
+
const applyState = (fn) => {
|
|
1009
|
+
if (mountedRef.current && !controller.signal.aborted) {
|
|
1010
|
+
fn();
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
applyState(() => {
|
|
1014
|
+
setLoading(true);
|
|
1015
|
+
setError(void 0);
|
|
1016
|
+
setProgress(0);
|
|
1017
|
+
});
|
|
1018
|
+
try {
|
|
1019
|
+
const result = await extensionRuntime.uploadFileToPluginStorage(transport, file, {
|
|
1020
|
+
pathPrefix: target.pathPrefix,
|
|
1021
|
+
fileName: target.fileName,
|
|
1022
|
+
contentType: target.contentType,
|
|
1023
|
+
maxSizeBytes,
|
|
1024
|
+
signal: controller.signal
|
|
1025
|
+
});
|
|
1026
|
+
applyState(() => {
|
|
1027
|
+
setProgress(1);
|
|
1028
|
+
setLoading(false);
|
|
1029
|
+
});
|
|
1030
|
+
return result;
|
|
1031
|
+
} catch (err) {
|
|
1032
|
+
const typed = extensionRuntime.toPluginStorageUploadError(err);
|
|
1033
|
+
applyState(() => {
|
|
1034
|
+
setError(typed);
|
|
1035
|
+
setLoading(false);
|
|
1036
|
+
setProgress(0);
|
|
1037
|
+
});
|
|
1038
|
+
throw typed;
|
|
1039
|
+
}
|
|
1040
|
+
},
|
|
1041
|
+
[transport, maxSizeBytes]
|
|
1042
|
+
);
|
|
1043
|
+
return { upload, progress, loading, error };
|
|
1044
|
+
}
|
|
988
1045
|
var OVERLAY_HOST_CONTRACT_VERSION = "1.0.0";
|
|
989
1046
|
var OverlayHostContext = react.createContext(null);
|
|
990
1047
|
function useOverlayHost() {
|
|
@@ -1414,6 +1471,26 @@ function withActiveNavHref(input, activeNavHref) {
|
|
|
1414
1471
|
return { ...input, activeNavHref };
|
|
1415
1472
|
}
|
|
1416
1473
|
|
|
1474
|
+
Object.defineProperty(exports, "PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES", {
|
|
1475
|
+
enumerable: true,
|
|
1476
|
+
get: function () { return extensionRuntime.PLUGIN_STORAGE_UPLOAD_CONTENT_TYPES; }
|
|
1477
|
+
});
|
|
1478
|
+
Object.defineProperty(exports, "PluginStorageUploadError", {
|
|
1479
|
+
enumerable: true,
|
|
1480
|
+
get: function () { return extensionRuntime.PluginStorageUploadError; }
|
|
1481
|
+
});
|
|
1482
|
+
Object.defineProperty(exports, "isPluginStorageUploadError", {
|
|
1483
|
+
enumerable: true,
|
|
1484
|
+
get: function () { return extensionRuntime.isPluginStorageUploadError; }
|
|
1485
|
+
});
|
|
1486
|
+
Object.defineProperty(exports, "resolvePluginStorageContentType", {
|
|
1487
|
+
enumerable: true,
|
|
1488
|
+
get: function () { return extensionRuntime.resolvePluginStorageContentType; }
|
|
1489
|
+
});
|
|
1490
|
+
Object.defineProperty(exports, "validatePluginStoragePrefix", {
|
|
1491
|
+
enumerable: true,
|
|
1492
|
+
get: function () { return extensionRuntime.validatePluginStoragePrefix; }
|
|
1493
|
+
});
|
|
1417
1494
|
exports.BaseMcpService = BaseMcpService;
|
|
1418
1495
|
exports.DEFAULT_MAX_UPLOAD_BYTES = DEFAULT_MAX_UPLOAD_BYTES;
|
|
1419
1496
|
exports.EHX_PLUGIN_PORTAL_ATTR = EHX_PLUGIN_PORTAL_ATTR;
|
|
@@ -1480,6 +1557,7 @@ exports.useSurfaceUrl = useSurfaceUrl;
|
|
|
1480
1557
|
exports.useToolInvoker = useToolInvoker;
|
|
1481
1558
|
exports.useToolInvokerMap = useToolInvokerMap;
|
|
1482
1559
|
exports.useTypedToolInvokerMap = useTypedToolInvokerMap;
|
|
1560
|
+
exports.useUploadToStorage = useUploadToStorage;
|
|
1483
1561
|
exports.useView = useView;
|
|
1484
1562
|
exports.withActiveNavHref = withActiveNavHref;
|
|
1485
1563
|
//# sourceMappingURL=index.cjs.map
|