@peers-app/peers-ui 0.19.0 → 0.19.7

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.d.ts CHANGED
@@ -6,6 +6,7 @@ export * from "./components/markdown-editor/editor-inline";
6
6
  export * from "./components/markdown-with-mentions";
7
7
  export * from "./components/sortable-list";
8
8
  export * from "./components/tabs";
9
+ export { Typeahead, type TypeaheadItem } from "./components/typeahead";
9
10
  export * from "./components/voice-indicator";
10
11
  export { mainContentPath } from "./globals";
11
12
  export * from "./hooks";
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.updateActiveTabTitle = exports.goToTabPath = exports.closeCurrentTab = exports.activeTabs = exports.activeTabId = exports.mainContentPath = exports.MarkdownEditor = void 0;
17
+ exports.updateActiveTabTitle = exports.goToTabPath = exports.closeCurrentTab = exports.activeTabs = exports.activeTabId = exports.mainContentPath = exports.Typeahead = exports.MarkdownEditor = void 0;
18
18
  __exportStar(require("./components/chat-overlay"), exports);
19
19
  __exportStar(require("./components/inverse-lazy-list"), exports);
20
20
  var editor_1 = require("./components/markdown-editor/editor");
@@ -23,6 +23,8 @@ __exportStar(require("./components/markdown-editor/editor-inline"), exports);
23
23
  __exportStar(require("./components/markdown-with-mentions"), exports);
24
24
  __exportStar(require("./components/sortable-list"), exports);
25
25
  __exportStar(require("./components/tabs"), exports);
26
+ var typeahead_1 = require("./components/typeahead");
27
+ Object.defineProperty(exports, "Typeahead", { enumerable: true, get: function () { return typeahead_1.Typeahead; } });
26
28
  __exportStar(require("./components/voice-indicator"), exports);
27
29
  var globals_1 = require("./globals");
28
30
  Object.defineProperty(exports, "mainContentPath", { enumerable: true, get: function () { return globals_1.mainContentPath; } });
@@ -1,5 +1,6 @@
1
1
  interface IProps {
2
2
  packageId: string;
3
3
  }
4
+ /** Detail screen for a single package with Info, Versions, Components, and Dependencies tabs. */
4
5
  export declare const PackageDetails: (props: IProps) => import("react/jsx-runtime").JSX.Element;
5
6
  export {};
@@ -1,12 +1,8 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.PackageDetails = void 0;
7
4
  const jsx_runtime_1 = require("react/jsx-runtime");
8
5
  const peers_sdk_1 = require("@peers-app/peers-sdk");
9
- const react_1 = __importDefault(require("react"));
10
6
  const input_1 = require("../../components/input");
11
7
  const loading_indicator_1 = require("../../components/loading-indicator");
12
8
  const save_button_1 = require("../../components/save-button");
@@ -15,9 +11,9 @@ const hooks_1 = require("../../hooks");
15
11
  const tabs_state_1 = require("../../tabs-layout/tabs-state");
16
12
  const package_info_1 = require("./package-info");
17
13
  const package_versions_1 = require("./package-versions");
14
+ /** Detail screen for a single package with Info, Versions, Components, and Dependencies tabs. */
18
15
  const PackageDetails = (props) => {
19
16
  const refresh = (0, hooks_1.useObservableState)(Date.now());
20
- const saveDeviceTagRef = react_1.default.useRef(null);
21
17
  const pkg = (0, hooks_1.usePromise)(async () => {
22
18
  const pkg = await (0, peers_sdk_1.Packages)().get(props.packageId);
23
19
  if (!pkg) {
@@ -61,11 +57,10 @@ const PackageDetails = (props) => {
61
57
  }
62
58
  return ((0, jsx_runtime_1.jsxs)("div", { className: "container-fluid p-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "d-flex", children: [(0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)("h4", { children: (0, jsx_runtime_1.jsx)("i", { className: "bi bi-box-fill me-2" }) }) }), (0, jsx_runtime_1.jsx)("div", { className: "flex-grow-1", children: (0, jsx_runtime_1.jsx)("h4", { children: (0, jsx_runtime_1.jsx)(input_1.Input, { className: "border border-0", style: { width: "100%", outline: "none", backgroundColor: "transparent" }, value: pkg.qs.name }, pkg.packageId) }) }), (0, jsx_runtime_1.jsx)("div", { children: (0, jsx_runtime_1.jsx)(save_button_1.SaveButton, { doc: pkg, onClick: async () => {
63
59
  await pkg.save();
64
- await saveDeviceTagRef.current?.();
65
60
  }, addActions: [...addActions] }, pkg.packageId) })] }), (0, jsx_runtime_1.jsx)(tabs_1.Tabs, { tabs: [
66
61
  {
67
62
  name: "Info",
68
- content: ((0, jsx_runtime_1.jsx)(tabs_1.ScreenTabBody, { children: (0, jsx_runtime_1.jsx)(package_info_1.PackageInfo, { pkg: pkg, saveDeviceTagRef: saveDeviceTagRef }) })),
63
+ content: ((0, jsx_runtime_1.jsx)(tabs_1.ScreenTabBody, { children: (0, jsx_runtime_1.jsx)(package_info_1.PackageInfo, { pkg: pkg }) })),
69
64
  },
70
65
  {
71
66
  name: "Versions",
@@ -0,0 +1,33 @@
1
+ import { type IPackageVersion } from "@peers-app/peers-sdk";
2
+ /** Severity of a version update: major > minor > patch. */
3
+ export type UpdateLevel = "major" | "minor" | "patch" | null;
4
+ /** Result of checking a package's version status against available versions. */
5
+ export interface IVersionStatus {
6
+ activePv: IPackageVersion;
7
+ newerLevel: UpdateLevel;
8
+ newestPv: IPackageVersion | null;
9
+ }
10
+ /**
11
+ * Check a package's version status: what's currently active, whether a newer
12
+ * version exists within the follow policy, and which version is the best
13
+ * upgrade candidate.
14
+ *
15
+ * @param packageId - The package to check.
16
+ * @param activePackageVersionId - The effective active version (device or group).
17
+ * @param followVersionTags - Tag filter from effective prefs.
18
+ */
19
+ export declare function checkVersionStatus(packageId: string, activePackageVersionId: string, followVersionTags: string | undefined): Promise<IVersionStatus | null>;
20
+ /** Whether the current user is an admin of the active group. */
21
+ export declare function isGroupAdmin(): Promise<boolean>;
22
+ /**
23
+ * Activate a specific package version, handling group-level vs device-local
24
+ * activation. When the device has no follow override and the user is a group
25
+ * admin, the group's `activePackageVersionId` is advanced so all devices
26
+ * stay in sync. Otherwise only the device-local prefs are updated.
27
+ *
28
+ * @param packageId - The package to activate a version for.
29
+ * @param pv - The package version to activate.
30
+ */
31
+ export declare function activatePackageVersion(packageId: string, pv: IPackageVersion): Promise<void>;
32
+ /** Numeric rank for sorting packages by update urgency. */
33
+ export declare const updateLevelRank: Record<string, number>;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateLevelRank = void 0;
4
+ exports.checkVersionStatus = checkVersionStatus;
5
+ exports.isGroupAdmin = isGroupAdmin;
6
+ exports.activatePackageVersion = activatePackageVersion;
7
+ const peers_sdk_1 = require("@peers-app/peers-sdk");
8
+ /**
9
+ * Check a package's version status: what's currently active, whether a newer
10
+ * version exists within the follow policy, and which version is the best
11
+ * upgrade candidate.
12
+ *
13
+ * @param packageId - The package to check.
14
+ * @param activePackageVersionId - The effective active version (device or group).
15
+ * @param followVersionTags - Tag filter from effective prefs.
16
+ */
17
+ async function checkVersionStatus(packageId, activePackageVersionId, followVersionTags) {
18
+ const all = await (0, peers_sdk_1.PackageVersions)().list({ packageId });
19
+ const active = all.find((v) => v.packageVersionId === activePackageVersionId);
20
+ if (!active)
21
+ return null;
22
+ let newerLevel = null;
23
+ let newestPv = null;
24
+ if (active.version) {
25
+ const parse = (v) => v.split(".").map(Number);
26
+ const [aMaj, aMin, aPat] = parse(active.version);
27
+ let bestVersion = active.version;
28
+ for (const v of all) {
29
+ if (!v.version || v.packageVersionId === activePackageVersionId)
30
+ continue;
31
+ if (!(0, peers_sdk_1.doesTagMatch)(active.versionTag, v.versionTag, followVersionTags, undefined))
32
+ continue;
33
+ const [maj, min, pat] = parse(v.version);
34
+ const [bMaj, bMin, bPat] = parse(bestVersion);
35
+ let candidateLevel = null;
36
+ if (maj > aMaj)
37
+ candidateLevel = "major";
38
+ else if (maj === aMaj && min > aMin)
39
+ candidateLevel = "minor";
40
+ else if (maj === aMaj && min === aMin && pat > aPat)
41
+ candidateLevel = "patch";
42
+ else if (maj === aMaj &&
43
+ min === aMin &&
44
+ pat === aPat &&
45
+ (v.createdAt || "") > (active.createdAt || ""))
46
+ candidateLevel = "patch";
47
+ if (!candidateLevel)
48
+ continue;
49
+ const levelRank = { major: 3, minor: 2, patch: 1 };
50
+ if (!newerLevel || levelRank[candidateLevel] > levelRank[newerLevel]) {
51
+ newerLevel = candidateLevel;
52
+ }
53
+ const isHigherThanBest = maj > bMaj ||
54
+ (maj === bMaj && min > bMin) ||
55
+ (maj === bMaj && min === bMin && pat > bPat) ||
56
+ (maj === bMaj &&
57
+ min === bMin &&
58
+ pat === bPat &&
59
+ (v.createdAt || "") > (newestPv?.createdAt || active.createdAt || ""));
60
+ if (!newestPv || isHigherThanBest) {
61
+ newestPv = v;
62
+ bestVersion = v.version;
63
+ }
64
+ }
65
+ }
66
+ return { activePv: active, newerLevel, newestPv };
67
+ }
68
+ /** Whether the current user is an admin of the active group. */
69
+ async function isGroupAdmin() {
70
+ try {
71
+ const userCtx = await (0, peers_sdk_1.getUserContext)();
72
+ const dc = userCtx.defaultDataContext();
73
+ const contextId = dc?.groupId || dc?.dataContextId;
74
+ if (!contextId)
75
+ return false;
76
+ const role = await (0, peers_sdk_1.getUserRole)(contextId, userCtx.userId);
77
+ return role >= peers_sdk_1.GroupMemberRole.Admin;
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ }
83
+ /**
84
+ * Activate a specific package version, handling group-level vs device-local
85
+ * activation. When the device has no follow override and the user is a group
86
+ * admin, the group's `activePackageVersionId` is advanced so all devices
87
+ * stay in sync. Otherwise only the device-local prefs are updated.
88
+ *
89
+ * @param packageId - The package to activate a version for.
90
+ * @param pv - The package version to activate.
91
+ */
92
+ async function activatePackageVersion(packageId, pv) {
93
+ const prefsVar = (0, peers_sdk_1.packagePrefsVar)(packageId);
94
+ await prefsVar.loadingPromise;
95
+ const devicePrefs = prefsVar();
96
+ const activatesGroup = pv.versionTag !== "dev" && !(0, peers_sdk_1.hasDeviceFollowOverride)(devicePrefs) && (await isGroupAdmin());
97
+ if (activatesGroup) {
98
+ const current = await (0, peers_sdk_1.Packages)().get(packageId);
99
+ if (current) {
100
+ current.activePackageVersionId = pv.packageVersionId;
101
+ await (0, peers_sdk_1.Packages)().signAndSave(current);
102
+ }
103
+ await (0, peers_sdk_1.updatePackagePrefs)(packageId, { activePackageVersionId: undefined });
104
+ }
105
+ else {
106
+ await (0, peers_sdk_1.updatePackagePrefs)(packageId, { activePackageVersionId: pv.packageVersionId });
107
+ }
108
+ }
109
+ /** Numeric rank for sorting packages by update urgency. */
110
+ exports.updateLevelRank = { major: 3, minor: 2, patch: 1 };
@@ -1,6 +1,5 @@
1
1
  import { type IDoc, type IPackage } from "@peers-app/peers-sdk";
2
- import React from "react";
2
+ /** Info tab for the package details screen. */
3
3
  export declare const PackageInfo: (props: {
4
4
  pkg: IDoc<IPackage>;
5
- saveDeviceTagRef?: React.MutableRefObject<(() => Promise<void>) | null>;
6
5
  }) => import("react/jsx-runtime").JSX.Element;
@@ -1,19 +1,17 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.PackageInfo = void 0;
7
4
  const jsx_runtime_1 = require("react/jsx-runtime");
8
5
  const peers_sdk_1 = require("@peers-app/peers-sdk");
9
- const react_1 = __importDefault(require("react"));
10
6
  const input_1 = require("../../components/input");
11
7
  const markdown_with_mentions_1 = require("../../components/markdown-with-mentions");
12
8
  const tooltip_1 = require("../../components/tooltip");
13
9
  const hooks_1 = require("../../hooks");
10
+ /** Info tab for the package details screen. */
14
11
  const PackageInfo = (props) => {
15
12
  const { pkg } = props;
16
13
  const [followVersionTags] = (0, hooks_1.useObservable)(pkg.qs.followVersionTags);
14
+ const [versionFollowRange] = (0, hooks_1.useObservable)(pkg.qs.versionFollowRange);
17
15
  const localPathVar = (0, peers_sdk_1.groupDeviceVar)(`packageLocalPath_${pkg.packageId}`, {
18
16
  defaultValue: `${peers_sdk_1.packagesRootDir}/${pkg.name}`,
19
17
  });
@@ -21,43 +19,13 @@ const PackageInfo = (props) => {
21
19
  const [devicePrefs] = (0, hooks_1.useObservable)((0, peers_sdk_1.packagePrefsVar)(pkg.packageId));
22
20
  const effective = (0, peers_sdk_1.getEffectivePackagePrefs)(pkg.toJS(), devicePrefs);
23
21
  const activeVersionId = effective.activePackageVersionId;
24
- const deviceFollowTags = devicePrefs?.followTags;
25
22
  const isPinned = effective.isPinned;
26
23
  const followRange = effective.followRange;
27
- const [deviceTagDraft, setDeviceTagDraft] = react_1.default.useState(deviceFollowTags || "");
28
- const savingRef = react_1.default.useRef(false);
29
- react_1.default.useEffect(() => {
30
- if (!savingRef.current) {
31
- setDeviceTagDraft(deviceFollowTags || "");
32
- }
33
- }, [deviceFollowTags]);
34
- const deviceTagDirty = deviceTagDraft.trim() !== (deviceFollowTags || "");
35
- const prevDirtyRef = react_1.default.useRef(false);
36
- react_1.default.useEffect(() => {
37
- if (deviceTagDirty && !prevDirtyRef.current) {
38
- pkg.q((pkg.q() || 0) + 1);
39
- }
40
- else if (!deviceTagDirty && prevDirtyRef.current) {
41
- pkg.q(Math.max(0, (pkg.q() || 0) - 1));
42
- }
43
- prevDirtyRef.current = deviceTagDirty;
44
- }, [deviceTagDirty, pkg.q]);
45
- if (props.saveDeviceTagRef) {
46
- props.saveDeviceTagRef.current = async () => {
47
- const val = deviceTagDraft.trim();
48
- if (val !== (deviceFollowTags || "")) {
49
- savingRef.current = true;
50
- try {
51
- await (0, peers_sdk_1.updatePackagePrefs)(pkg.packageId, {
52
- followTags: val || undefined,
53
- });
54
- }
55
- finally {
56
- savingRef.current = false;
57
- }
58
- }
59
- };
60
- }
24
+ const hasOverride = (0, peers_sdk_1.hasDeviceFollowOverride)(devicePrefs);
25
+ const groupFollowTags = followVersionTags === "stable,beta" ? "stable,beta" : "stable";
26
+ const groupRange = versionFollowRange || "latest";
27
+ const effectiveFollowTags = effective.followTags || "stable";
28
+ const followTagsValue = effectiveFollowTags === "stable,beta" ? "stable,beta" : "stable";
61
29
  const activeVersion = (0, hooks_1.usePromise)(async () => {
62
30
  if (!activeVersionId)
63
31
  return null;
@@ -70,7 +38,6 @@ const PackageInfo = (props) => {
70
38
  const active = all.find((v) => v.packageVersionId === activeVersionId);
71
39
  if (!active?.version)
72
40
  return "uptodate";
73
- const effectiveFollowTags = deviceFollowTags || followVersionTags;
74
41
  const parse = (v) => v.split(".").map(Number);
75
42
  const [aMaj, aMin, aPat] = parse(active.version);
76
43
  let highest = null;
@@ -99,18 +66,56 @@ const PackageInfo = (props) => {
99
66
  }
100
67
  }
101
68
  return highest || "uptodate";
102
- }, undefined, [activeVersionId, pkg.packageId, followVersionTags, deviceFollowTags]);
69
+ }, undefined, [activeVersionId, pkg.packageId, effectiveFollowTags]);
103
70
  const remoteRepoUrl = pkg.remoteRepo;
104
71
  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: () => {
105
72
  peers_sdk_1.rpcServerCalls.openPackage(localPath || peers_sdk_1.packagesRootDir);
106
73
  }, 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 or a link to the Peers page for the package.` }), (0, jsx_runtime_1.jsx)("button", { className: "btn btn-sm btn-link", onClick: () => {
107
74
  peers_sdk_1.rpcServerCalls.openLinkInBrowser(remoteRepoUrl);
108
- }, children: "Open Remote" })] })] }), (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.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 (this device):", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Controls which new versions are auto-activated on this device. **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: isPinned ? "pinned" : followRange, onChange: async (e) => {
75
+ }, children: "Open Remote" })] })] }), (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.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: async (e) => {
76
+ const val = e.target.value;
77
+ const current = await (0, peers_sdk_1.Packages)().get(pkg.packageId);
78
+ if (current) {
79
+ current.versionFollowRange = val;
80
+ await (0, peers_sdk_1.Packages)().signAndSave(current);
81
+ await pkg.load();
82
+ }
83
+ }, 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: async (e) => {
109
84
  const val = e.target.value;
110
- await (0, peers_sdk_1.updatePackagePrefs)(pkg.packageId, {
111
- pinned: val === "pinned",
112
- followRange: val === "pinned" ? undefined : val,
113
- });
114
- }, 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 ${isPinned ? "opacity-50" : ""}`, children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Follow Version Tags (this device):", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Which version tags to auto-activate on this device. Leave empty to follow the group's tag policy. Use `*` for any tag, or a comma-separated list like `stable,beta`." })] }), (0, jsx_runtime_1.jsxs)("div", { className: "d-flex align-items-center gap-2", children: [(0, jsx_runtime_1.jsx)("input", { type: "text", className: "form-control form-control-sm p-0 ps-2", placeholder: `Following "${activeVersion?.versionTag || "stable"}"`, value: deviceTagDraft, onChange: (e) => setDeviceTagDraft(e.target.value), disabled: isPinned }), deviceTagDraft && ((0, jsx_runtime_1.jsx)("button", { className: "btn btn-sm btn-outline-secondary", title: "Clear device tag override", onClick: () => setDeviceTagDraft(""), children: (0, jsx_runtime_1.jsx)("i", { className: "bi bi-x-lg" }) }))] })] })] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsx)("hr", {}), (0, jsx_runtime_1.jsxs)("small", { children: ["Description:", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `This should be edited in the package's README.md. It will automatically update when you restart Peers or reload the package.` })] }), (0, jsx_runtime_1.jsx)(markdown_with_mentions_1.MarkdownWithMentions, { content: pkg.description || "" })] })] }));
85
+ const current = await (0, peers_sdk_1.Packages)().get(pkg.packageId);
86
+ if (current) {
87
+ current.followVersionTags = val === "stable" ? undefined : val;
88
+ await (0, peers_sdk_1.Packages)().signAndSave(current);
89
+ await pkg.load();
90
+ }
91
+ }, children: [(0, jsx_runtime_1.jsx)("option", { value: "stable", children: "Stable" }), (0, jsx_runtime_1.jsx)("option", { value: "stable,beta", children: "Stable + Beta" })] })] }), (0, jsx_runtime_1.jsx)("div", { className: "mb-3", children: (0, jsx_runtime_1.jsxs)("div", { className: "form-check", children: [(0, jsx_runtime_1.jsx)("input", { className: "form-check-input", type: "checkbox", id: `deviceOverride_${pkg.packageId}`, checked: hasOverride, onChange: async (e) => {
92
+ if (e.target.checked) {
93
+ await (0, peers_sdk_1.updatePackagePrefs)(pkg.packageId, {
94
+ followRange: groupRange === "pinned"
95
+ ? undefined
96
+ : groupRange,
97
+ followTags: followVersionTags || undefined,
98
+ pinned: groupRange === "pinned" ? true : undefined,
99
+ });
100
+ }
101
+ else {
102
+ await (0, peers_sdk_1.updatePackagePrefs)(pkg.packageId, {
103
+ followRange: undefined,
104
+ followTags: undefined,
105
+ pinned: undefined,
106
+ });
107
+ }
108
+ } }), (0, jsx_runtime_1.jsx)("label", { className: "form-check-label", htmlFor: `deviceOverride_${pkg.packageId}`, children: (0, jsx_runtime_1.jsx)("small", { children: "Override on this device" }) }), (0, jsx_runtime_1.jsx)("small", { children: (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `**Off:** This device follows the group's auto-update and channel settings. When an admin device upgrades, the group's active version advances for all devices.\n\n**On:** This device uses its own settings below. Upgrades only affect this device — the group's active version is not changed.` }) })] }) }), hasOverride && ((0, jsx_runtime_1.jsxs)("div", { className: "ps-3 border-start mb-3", children: [(0, jsx_runtime_1.jsxs)("div", { className: "mb-3", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Auto-Update Range (this device):", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Controls which new versions are auto-activated on **this device only**. Upgrades here do not change the group's active version." })] }), (0, jsx_runtime_1.jsxs)("select", { className: "form-select form-select-sm", value: isPinned ? "pinned" : followRange, onChange: async (e) => {
109
+ const val = e.target.value;
110
+ await (0, peers_sdk_1.updatePackagePrefs)(pkg.packageId, {
111
+ pinned: val === "pinned",
112
+ followRange: val === "pinned" ? undefined : val,
113
+ });
114
+ }, 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: isPinned ? "opacity-50" : "", children: [(0, jsx_runtime_1.jsxs)("small", { children: ["Following (this device):", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: "Which release channel to follow on **this device only**, overriding the group setting." })] }), (0, jsx_runtime_1.jsxs)("select", { className: "form-select form-select-sm", value: followTagsValue, disabled: isPinned, onChange: async (e) => {
115
+ const val = e.target.value;
116
+ await (0, peers_sdk_1.updatePackagePrefs)(pkg.packageId, {
117
+ followTags: val === "stable" ? "stable" : val,
118
+ });
119
+ }, children: [(0, jsx_runtime_1.jsx)("option", { value: "stable", children: "Stable" }), (0, jsx_runtime_1.jsx)("option", { value: "stable,beta", children: "Stable + Beta" })] })] })] }))] }), (0, jsx_runtime_1.jsxs)("div", { className: "mt-2", children: [(0, jsx_runtime_1.jsx)("hr", {}), (0, jsx_runtime_1.jsxs)("small", { children: ["Description:", (0, jsx_runtime_1.jsx)(tooltip_1.Tooltip, { markdownContent: `This should be edited in the package's README.md. It will automatically update when you restart Peers or reload the package.` })] }), (0, jsx_runtime_1.jsx)(markdown_with_mentions_1.MarkdownWithMentions, { content: pkg.description || "" })] })] }));
115
120
  };
116
121
  exports.PackageInfo = PackageInfo;