@peers-app/peers-ui 0.29.0 → 0.29.2
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/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/use-file-url.d.ts +23 -0
- package/dist/hooks/use-file-url.js +52 -0
- package/dist/screens/packages/package-info.js +4 -2
- package/dist/ui-router/bundle-files.d.ts +8 -0
- package/dist/ui-router/bundle-files.js +28 -0
- package/dist/ui-router/routes-loader.js +3 -2
- package/dist/ui-router/ui-loader.js +2 -1
- package/package.json +3 -3
package/dist/hooks/index.d.ts
CHANGED
package/dist/hooks/index.js
CHANGED
|
@@ -15,6 +15,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
// `use-host-contract` stays internal: its screens import it by path.
|
|
18
|
+
__exportStar(require("./use-file-url"), exports);
|
|
18
19
|
__exportStar(require("./use-observable"), exports);
|
|
19
20
|
__exportStar(require("./use-observable-state"), exports);
|
|
20
21
|
__exportStar(require("./use-on-screen"), exports);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** What {@link useFileUrl} returns. */
|
|
2
|
+
export interface IUseFileUrlResult {
|
|
3
|
+
/** An object URL for the file, such as an `<img src>`. Undefined until it's read. */
|
|
4
|
+
url: string | undefined;
|
|
5
|
+
/** True while the file is being read. */
|
|
6
|
+
loading: boolean;
|
|
7
|
+
/** Why the file couldn't be read: not found, or not on any connected device. */
|
|
8
|
+
error: Error | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Read a file saved with System Files and give a URL for it, for an image or a download link.
|
|
12
|
+
*
|
|
13
|
+
* ```tsx
|
|
14
|
+
* const avatar = useFileUrl(member.imageFileId);
|
|
15
|
+
* {avatar.url ? <img src={avatar.url} alt="" /> : <Initials />}
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Reads through `hostContract(SystemFiles)` in the screen's data context, the active group.
|
|
19
|
+
* The URL is revoked when the file id changes or the component unmounts, so don't keep it.
|
|
20
|
+
*
|
|
21
|
+
* @param fileId - The file's id; nothing is read while it's undefined.
|
|
22
|
+
*/
|
|
23
|
+
export declare function useFileUrl(fileId: string | undefined): IUseFileUrlResult;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useFileUrl = useFileUrl;
|
|
4
|
+
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
/**
|
|
7
|
+
* Read a file saved with System Files and give a URL for it, for an image or a download link.
|
|
8
|
+
*
|
|
9
|
+
* ```tsx
|
|
10
|
+
* const avatar = useFileUrl(member.imageFileId);
|
|
11
|
+
* {avatar.url ? <img src={avatar.url} alt="" /> : <Initials />}
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Reads through `hostContract(SystemFiles)` in the screen's data context, the active group.
|
|
15
|
+
* The URL is revoked when the file id changes or the component unmounts, so don't keep it.
|
|
16
|
+
*
|
|
17
|
+
* @param fileId - The file's id; nothing is read while it's undefined.
|
|
18
|
+
*/
|
|
19
|
+
function useFileUrl(fileId) {
|
|
20
|
+
const [state, setState] = (0, react_1.useState)({
|
|
21
|
+
url: undefined,
|
|
22
|
+
loading: !!fileId,
|
|
23
|
+
error: undefined,
|
|
24
|
+
});
|
|
25
|
+
(0, react_1.useEffect)(() => {
|
|
26
|
+
if (!fileId) {
|
|
27
|
+
setState({ url: undefined, loading: false, error: undefined });
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let ended = false;
|
|
31
|
+
let url;
|
|
32
|
+
setState({ url: undefined, loading: true, error: undefined });
|
|
33
|
+
const files = (0, peers_sdk_1.hostContract)(peers_sdk_1.SystemFiles);
|
|
34
|
+
(0, peers_sdk_1.readWholeFile)((input) => files.tools.read(input), fileId).then(({ bytes, mimeType }) => {
|
|
35
|
+
if (ended)
|
|
36
|
+
return;
|
|
37
|
+
url = URL.createObjectURL(new Blob([bytes], mimeType ? { type: mimeType } : undefined));
|
|
38
|
+
setState({ url, loading: false, error: undefined });
|
|
39
|
+
}, (error) => {
|
|
40
|
+
if (ended)
|
|
41
|
+
return;
|
|
42
|
+
const reason = error instanceof Error ? error : new Error(String(error));
|
|
43
|
+
setState({ url: undefined, loading: false, error: reason });
|
|
44
|
+
});
|
|
45
|
+
return () => {
|
|
46
|
+
ended = true;
|
|
47
|
+
if (url)
|
|
48
|
+
URL.revokeObjectURL(url);
|
|
49
|
+
};
|
|
50
|
+
}, [fileId]);
|
|
51
|
+
return state;
|
|
52
|
+
}
|
|
@@ -84,9 +84,11 @@ const PackageInfo = (props) => {
|
|
|
84
84
|
};
|
|
85
85
|
return ((0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("small", { children: "Name:" }), (0, jsx_runtime_1.jsx)(input_1.Input, { value: pkg.qs.name, className: "form-control mb-3 p-0 ps-2", placeholder: "Package name", title: "Package name", disabled: true }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Local Path:", (0, jsx_runtime_1.jsxs)("small", { children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `The local path to the directory containing the package. This will open in VS Code if it's installed otherwise this will open in your native file system` }), (0, jsx_runtime_1.jsx)("button", { className: "btn btn-sm btn-link", onClick: () => {
|
|
86
86
|
void (0, host_shell_consumer_1.openInEditorOnHost)(localPath || peers_sdk_1.packagesRootDir);
|
|
87
|
-
}, children: "Open Local" })] })] }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "form-control mb-3 p-0 ps-2", placeholder: "~/peers/packages/my-package", value: localPath || "", onChange: (e) => setLocalPath(e.target.value) })] }), remoteRepoUrl ? ((0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Source URL:", (0, jsx_runtime_1.jsxs)("small", { children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `The remote source of this package. Often a github repository URL
|
|
87
|
+
}, children: "Open Local" })] })] }), (0, jsx_runtime_1.jsx)("input", { type: "text", className: "form-control mb-3 p-0 ps-2", placeholder: "~/peers/packages/my-package", value: localPath || "", onChange: (e) => setLocalPath(e.target.value) })] }), remoteRepoUrl ? ((0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Source URL:", (0, jsx_runtime_1.jsxs)("small", { children: [(0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `The remote source of this package. Often a github repository URL, a link to the Peers page for the package, or a **peers://** URL shared through the Git package (clone it with **git clone**).` }), /^https?:\/\//.test(remoteRepoUrl) ? ((0, jsx_runtime_1.jsx)("button", { className: "btn btn-sm btn-link", onClick: () => {
|
|
88
88
|
void (0, host_shell_consumer_1.openOnHost)(remoteRepoUrl);
|
|
89
|
-
}, children: "Open Remote" })
|
|
89
|
+
}, children: "Open Remote" })) : ((0, jsx_runtime_1.jsx)("button", { className: "btn btn-sm btn-link", onClick: () => {
|
|
90
|
+
void navigator.clipboard?.writeText(remoteRepoUrl);
|
|
91
|
+
}, children: "Copy" }))] })] }), (0, jsx_runtime_1.jsx)(input_1.Input, { value: pkg.qs.remoteRepo, className: "form-control mb-3 p-0 ps-2", disabled: true })] })) : null, (0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Update URL:", (0, jsx_runtime_1.jsx)("small", { children: (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `Base URL for remote package updates. Admin devices poll **<updateUrl>/latest-<tag>.json** on startup (for example **latest-stable.json**). Signed artifacts must be published to this host separately — for example via the publish-package tool or the peers-core full-release pipeline.` }) })] }), (0, jsx_runtime_1.jsx)(input_1.Input, { value: pkg.qs.updateUrl, className: "form-control mb-3 p-0 ps-2", placeholder: "https://example.com/my-package", title: "Update URL" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsx)("hr", {}), (0, jsx_runtime_1.jsxs)("small", { children: ["Version:", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "The currently active version of this package. Manage versions in the Versions tab." })] }), activeVersion ? ((0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center mt-1 mb-3", children: [(0, jsx_runtime_1.jsxs)("strong", { className: "me-2", children: ["v", activeVersion.version] }), activeVersion.versionTag && ((0, jsx_runtime_1.jsx)("span", { className: `badge ${activeVersion.versionTag === "dev" ? "text-bg-danger" : activeVersion.versionTag.startsWith("beta") ? "text-bg-warning" : "text-bg-success"} me-2`, children: activeVersion.versionTag })), (0, jsx_runtime_1.jsx)("code", { className: "text-muted small me-2", children: activeVersion.packageVersionHash?.substring(0, 8) }), newerLevel === "uptodate" ? ((0, jsx_runtime_1.jsx)("span", { className: "badge text-bg-success", children: "Up to date" })) : newerLevel ? ((0, jsx_runtime_1.jsxs)("span", { className: `badge text-bg-${newerLevel === "major" ? "danger" : newerLevel === "minor" ? "warning" : "info"}`, children: ["Newer ", newerLevel, " version available"] })) : null] })) : ((0, jsx_runtime_1.jsx)("div", { className: "text-muted small mt-1 mb-3", children: "No active version" })), (0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Auto-Update Range:", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Controls which new versions are auto-activated for the group. When an admin device auto-upgrades, the group's active version advances for everyone. **Pinned** = never auto-update. **Patch** = same major.minor (e.g. 1.2.x). **Minor** = same major (e.g. 1.x.x). **Latest** = always auto-update to newest." })] }), (0, jsx_runtime_1.jsxs)("select", { className: "form-select form-select-sm", value: groupRange, onChange: (e) => saveFollowPolicy({
|
|
90
92
|
versionFollowRange: e.target.value,
|
|
91
93
|
}), children: [(0, jsx_runtime_1.jsx)("option", { value: "latest", children: "Latest (auto-update to newest)" }), (0, jsx_runtime_1.jsx)("option", { value: "minor", children: "Minor (same major version)" }), (0, jsx_runtime_1.jsx)("option", { value: "patch", children: "Patch (same major.minor version)" }), (0, jsx_runtime_1.jsx)("option", { value: "pinned", children: "Pinned (no auto-updates)" })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: `mb-3 ${groupRange === "pinned" ? "opacity-50" : ""}`, children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Following:", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Which release channel the group follows. Devices using group settings will auto-upgrade within this channel. **Stable** = only fully promoted releases. **Stable + Beta** = also includes beta pre-releases for early testing." })] }), (0, jsx_runtime_1.jsxs)("select", { className: "form-select form-select-sm", value: groupFollowTags, disabled: groupRange === "pinned", onChange: (e) => saveFollowPolicy({
|
|
92
94
|
followVersionTags: e.target.value === "stable" ? undefined : e.target.value,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read a package's UI or routes bundle as text, through System Files in the context its
|
|
3
|
+
* version is installed in.
|
|
4
|
+
*
|
|
5
|
+
* @param dataContextId - Context of the selected package version.
|
|
6
|
+
* @param fileId - The bundle's file id.
|
|
7
|
+
*/
|
|
8
|
+
export declare function readBundleText(dataContextId: string, fileId: string): Promise<string>;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readBundleText = readBundleText;
|
|
4
|
+
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
|
+
const clients = new Map();
|
|
6
|
+
/** System Files on the local host, scoped to one data context; one client per context. */
|
|
7
|
+
function filesIn(dataContextId) {
|
|
8
|
+
let client = clients.get(dataContextId);
|
|
9
|
+
if (!client) {
|
|
10
|
+
client = (0, peers_sdk_1.hostContract)(peers_sdk_1.SystemFiles, { dataContextId: async () => dataContextId });
|
|
11
|
+
clients.set(dataContextId, client);
|
|
12
|
+
}
|
|
13
|
+
return client;
|
|
14
|
+
}
|
|
15
|
+
// Keep a leading BOM, as Buffer's UTF-8 decoding did, so bundle integrity hashes still match.
|
|
16
|
+
const decoder = new TextDecoder("utf-8", { ignoreBOM: true });
|
|
17
|
+
/**
|
|
18
|
+
* Read a package's UI or routes bundle as text, through System Files in the context its
|
|
19
|
+
* version is installed in.
|
|
20
|
+
*
|
|
21
|
+
* @param dataContextId - Context of the selected package version.
|
|
22
|
+
* @param fileId - The bundle's file id.
|
|
23
|
+
*/
|
|
24
|
+
async function readBundleText(dataContextId, fileId) {
|
|
25
|
+
const files = filesIn(dataContextId);
|
|
26
|
+
const { bytes } = await (0, peers_sdk_1.readWholeFile)((input) => files.tools.read(input), fileId);
|
|
27
|
+
return decoder.decode(bytes);
|
|
28
|
+
}
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.loadAllRoutes = exports.allPackages = void 0;
|
|
4
4
|
const peers_sdk_1 = require("@peers-app/peers-sdk");
|
|
5
5
|
require("../ui-defaults");
|
|
6
|
+
const bundle_files_1 = require("./bundle-files");
|
|
6
7
|
const runnable_version_1 = require("./runnable-version");
|
|
7
8
|
/** Packages with authorized navigation metadata available in the current context. */
|
|
8
9
|
exports.allPackages = (0, peers_sdk_1.observable)([]);
|
|
@@ -102,8 +103,8 @@ function loadRoutesBundle(pkg, forceRefresh) {
|
|
|
102
103
|
const selected = await (0, runnable_version_1.resolveRunnableVersion)(pkg);
|
|
103
104
|
const routesBundleFileId = selected?.pv.routesBundleFileId;
|
|
104
105
|
let bundleCode = "";
|
|
105
|
-
if (routesBundleFileId) {
|
|
106
|
-
bundleCode = await
|
|
106
|
+
if (selected && routesBundleFileId) {
|
|
107
|
+
bundleCode = await (0, bundle_files_1.readBundleText)(selected.dataContext.dataContextId, routesBundleFileId);
|
|
107
108
|
}
|
|
108
109
|
if (bundleCode) {
|
|
109
110
|
if (selected?.dataContext.groupId)
|
|
@@ -43,6 +43,7 @@ const loading_indicator_1 = require("../components/loading-indicator");
|
|
|
43
43
|
const globals_1 = require("../globals");
|
|
44
44
|
const hooks_1 = require("../hooks");
|
|
45
45
|
require("../ui-defaults");
|
|
46
|
+
const bundle_files_1 = require("./bundle-files");
|
|
46
47
|
const routes_loader_1 = require("./routes-loader");
|
|
47
48
|
const runnable_version_1 = require("./runnable-version");
|
|
48
49
|
const peersUIs = {};
|
|
@@ -194,7 +195,7 @@ function loadUIBundle(pkg, forceRefresh) {
|
|
|
194
195
|
if (!selected || !uiBundleFileId) {
|
|
195
196
|
return;
|
|
196
197
|
}
|
|
197
|
-
const bundleCode = await
|
|
198
|
+
const bundleCode = await (0, bundle_files_1.readBundleText)(selected.dataContext.dataContextId, uiBundleFileId);
|
|
198
199
|
if (bundleCode) {
|
|
199
200
|
if (selected.dataContext.groupId)
|
|
200
201
|
(0, peers_sdk_1.assertPackageBundleIntegrity)(selected.pv, "ui", bundleCode);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peers-app/peers-ui",
|
|
3
|
-
"version": "0.29.
|
|
3
|
+
"version": "0.29.2",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/peers-app/peers-ui.git"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"lint:fix": "biome check --write ."
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"@peers-app/peers-sdk": "^0.29.
|
|
39
|
+
"@peers-app/peers-sdk": "^0.29.2",
|
|
40
40
|
"bootstrap": "^5.3.3",
|
|
41
41
|
"react": "^18.0.0",
|
|
42
42
|
"react-dom": "^18.0.0"
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"@babel/preset-react": "^7.24.1",
|
|
48
48
|
"@babel/preset-typescript": "^7.27.1",
|
|
49
49
|
"@biomejs/biome": "2.3.11",
|
|
50
|
-
"@peers-app/peers-sdk": "0.29.
|
|
50
|
+
"@peers-app/peers-sdk": "0.29.2",
|
|
51
51
|
"@testing-library/dom": "^10.4.0",
|
|
52
52
|
"@testing-library/jest-dom": "^6.6.3",
|
|
53
53
|
"@testing-library/react": "^16.3.0",
|