@peers-app/peers-ui 0.10.1 → 0.10.4

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.
@@ -1,41 +1,11 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
35
5
  Object.defineProperty(exports, "__esModule", { value: true });
36
6
  exports.AssistantDetails = void 0;
37
7
  const peers_sdk_1 = require("@peers-app/peers-sdk");
38
- const react_1 = __importStar(require("react"));
8
+ const react_1 = __importDefault(require("react"));
39
9
  const input_1 = require("../../components/input");
40
10
  const loading_indicator_1 = require("../../components/loading-indicator");
41
11
  const save_button_1 = require("../../components/save-button");
@@ -46,15 +16,13 @@ const assistant_config_1 = require("./assistant-config");
46
16
  const assistant_info_1 = require("./assistant-info");
47
17
  const assistant_tools_1 = require("./assistant-tools");
48
18
  const AssistantDetails = (props) => {
49
- const [isPrimaryAssistant] = (0, react_1.useState)(() => (0, peers_sdk_1.observable)(false));
19
+ const [userAssistantId] = (0, hooks_1.useObservable)(peers_sdk_1.userPrimaryAssistantVar);
20
+ const [groupAssistantId] = (0, hooks_1.useObservable)(peers_sdk_1.groupPrimaryAssistantVar);
50
21
  const assistant = (0, hooks_1.usePromise)((async () => {
51
22
  const assistant = await (0, peers_sdk_1.Assistants)().get(props.assistantId);
52
23
  if (!assistant) {
53
24
  return null;
54
25
  }
55
- const primaryAssistantId = (await (0, peers_sdk_1.getPrimaryAssistant)()).assistantId
56
- || peers_sdk_1.defaultAssistantId;
57
- isPrimaryAssistant(assistant.assistantId === primaryAssistantId);
58
26
  (0, tabs_state_1.updateActiveTabTitle)(assistant.name || "Assistant");
59
27
  return (0, peers_sdk_1.Assistants)().initDoc(assistant);
60
28
  }), undefined, [props.assistantId]);
@@ -77,7 +45,10 @@ const AssistantDetails = (props) => {
77
45
  react_1.default.createElement("div", { className: "" },
78
46
  react_1.default.createElement(save_button_1.SaveButton, { key: assistant.assistantId, doc: assistant }))),
79
47
  react_1.default.createElement(tabs_1.Tabs, { key: assistant.assistantId, tabs: [
80
- { name: 'Info', content: react_1.default.createElement(assistant_info_1.AssistantInfo, { assistant: assistant, isPrimaryAssistant: isPrimaryAssistant }) },
48
+ {
49
+ name: 'Info',
50
+ content: (react_1.default.createElement(assistant_info_1.AssistantInfo, { assistant: assistant, isPrimaryAssistant: userAssistantId === assistant.assistantId, isGroupPrimaryAssistant: groupAssistantId === assistant.assistantId, onSetPrimary: (checked) => (0, peers_sdk_1.userPrimaryAssistantVar)(checked ? assistant.assistantId : undefined), onSetGroupPrimary: (checked) => (0, peers_sdk_1.groupPrimaryAssistantVar)(checked ? assistant.assistantId : undefined) })),
51
+ },
81
52
  { name: 'Config', content: react_1.default.createElement(assistant_config_1.AssistantConfig, { assistant: assistant }) },
82
53
  { name: 'Tools', content: react_1.default.createElement(assistant_tools_1.AssistantTools, { assistant: assistant }), },
83
54
  ] })));
@@ -1,6 +1,11 @@
1
- import { IAssistant, IDoc, Observable } from "@peers-app/peers-sdk";
1
+ import { IAssistant, IDoc } from "@peers-app/peers-sdk";
2
2
  import React from "react";
3
- export declare const AssistantInfo: (props: {
3
+ interface AssistantInfoProps {
4
4
  assistant: IDoc<IAssistant>;
5
- isPrimaryAssistant: Observable<boolean>;
6
- }) => React.JSX.Element;
5
+ isPrimaryAssistant: boolean;
6
+ isGroupPrimaryAssistant: boolean;
7
+ onSetPrimary: (checked: boolean) => void;
8
+ onSetGroupPrimary: (checked: boolean) => void;
9
+ }
10
+ export declare const AssistantInfo: (props: AssistantInfoProps) => React.JSX.Element;
11
+ export {};
@@ -7,20 +7,17 @@ exports.AssistantInfo = void 0;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const input_1 = require("../../components/input");
9
9
  const editor_inline_1 = require("../../components/markdown-editor/editor-inline");
10
- const hooks_1 = require("../../hooks");
11
10
  const AssistantInfo = (props) => {
12
- const { assistant, isPrimaryAssistant } = props;
13
- (0, hooks_1.useObservable)(isPrimaryAssistant);
11
+ const { assistant, isPrimaryAssistant, isGroupPrimaryAssistant, onSetPrimary, onSetGroupPrimary } = props;
14
12
  return (react_1.default.createElement("div", null,
15
13
  react_1.default.createElement("small", null, "Name:"),
16
14
  react_1.default.createElement(input_1.Input, { value: assistant.qs.name, className: "form-control mb-3 p-0 ps-2", placeholder: "Tool name", title: "Tool name" }),
17
15
  react_1.default.createElement("div", null,
18
- react_1.default.createElement("label", { htmlFor: "isPrimary", className: "form-label small mt-2 me-2" }, "Primary Assistant:"),
19
- react_1.default.createElement("input", { type: "checkbox", checked: isPrimaryAssistant(), onChange: (e) => {
20
- const checked = e.target.checked;
21
- isPrimaryAssistant(checked);
22
- assistant.q(assistant.q() + 1);
23
- } })),
16
+ react_1.default.createElement("label", { htmlFor: "isPrimary", className: "form-label small mt-2 me-2" }, "My Default Assistant:"),
17
+ react_1.default.createElement("input", { type: "checkbox", id: "isPrimary", checked: isPrimaryAssistant, onChange: (e) => onSetPrimary(e.target.checked) })),
18
+ react_1.default.createElement("div", null,
19
+ react_1.default.createElement("label", { htmlFor: "isGroupPrimary", className: "form-label small mt-2 me-2" }, "Group Default Assistant:"),
20
+ react_1.default.createElement("input", { type: "checkbox", id: "isGroupPrimary", checked: isGroupPrimaryAssistant, onChange: (e) => onSetGroupPrimary(e.target.checked) })),
24
21
  react_1.default.createElement("div", { className: 'mt-2' },
25
22
  react_1.default.createElement("small", null, "Description:"),
26
23
  react_1.default.createElement(editor_inline_1.MarkdownEditorInline, { value: assistant.qs.description }))));
@@ -72,6 +72,8 @@ const PackageInfo = (props) => {
72
72
  for (const v of all) {
73
73
  if (!v.version || v.packageVersionId === activeVersionId)
74
74
  continue;
75
+ if (!(0, peers_sdk_1.doesTagMatch)(active.versionTag, v.versionTag, followVersionTags, deviceTag))
76
+ continue;
75
77
  const [maj, min, pat] = parse(v.version);
76
78
  if (maj > aMaj)
77
79
  return 'major';
@@ -88,7 +90,7 @@ const PackageInfo = (props) => {
88
90
  }
89
91
  }
90
92
  return highest || 'uptodate';
91
- }, undefined, [activeVersionId, pkg.packageId]);
93
+ }, undefined, [activeVersionId, pkg.packageId, followVersionTags, deviceTag]);
92
94
  const isPinned = versionFollowRange === 'pinned';
93
95
  return (react_1.default.createElement("div", null,
94
96
  react_1.default.createElement("small", null, "Name:"),
@@ -111,12 +113,6 @@ const PackageInfo = (props) => {
111
113
  peers_sdk_1.rpcServerCalls.openLinkInBrowser(pkg.remoteRepo);
112
114
  } }, "Open Remote"))),
113
115
  react_1.default.createElement(input_1.Input, { value: pkg.qs.remoteRepo, className: "form-control mb-3 p-0 ps-2", disabled: true }))),
114
- react_1.default.createElement("div", { className: "mt-2" },
115
- react_1.default.createElement("hr", null),
116
- react_1.default.createElement("small", null,
117
- "Description:",
118
- react_1.default.createElement(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.` })),
119
- react_1.default.createElement(markdown_with_mentions_1.MarkdownWithMentions, { content: pkg.description || '' })),
120
116
  react_1.default.createElement("div", { className: "mt-2" },
121
117
  react_1.default.createElement("hr", null),
122
118
  react_1.default.createElement("small", null,
@@ -156,6 +152,12 @@ const PackageInfo = (props) => {
156
152
  react_1.default.createElement("div", { className: "d-flex align-items-center gap-2" },
157
153
  react_1.default.createElement("input", { type: "text", className: "form-control form-control-sm p-0 ps-2", placeholder: "e.g. beta", value: deviceTagDraft, onChange: (e) => setDeviceTagDraft(e.target.value) }),
158
154
  deviceTagDraft && (react_1.default.createElement("button", { className: "btn btn-sm btn-outline-secondary", title: "Clear device tag override", onClick: () => setDeviceTagDraft('') },
159
- react_1.default.createElement("i", { className: "bi bi-x-lg" }))))))));
155
+ react_1.default.createElement("i", { className: "bi bi-x-lg" })))))),
156
+ react_1.default.createElement("div", { className: "mt-2" },
157
+ react_1.default.createElement("hr", null),
158
+ react_1.default.createElement("small", null,
159
+ "Description:",
160
+ react_1.default.createElement(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.` })),
161
+ react_1.default.createElement(markdown_with_mentions_1.MarkdownWithMentions, { content: pkg.description || '' }))));
160
162
  };
161
163
  exports.PackageInfo = PackageInfo;
@@ -43,19 +43,54 @@ const tooltip_1 = require("../../components/tooltip");
43
43
  const globals_1 = require("../../globals");
44
44
  const hooks_1 = require("../../hooks");
45
45
  const ui_loader_1 = require("../../ui-router/ui-loader");
46
- const PackageVersionBadge = ({ activePackageVersionId }) => {
47
- const pv = (0, hooks_1.usePromise)(async () => {
46
+ const PackageVersionBadge = ({ activePackageVersionId, packageId, followVersionTags }) => {
47
+ const data = (0, hooks_1.usePromise)(async () => {
48
48
  if (!activePackageVersionId)
49
49
  return null;
50
- return (0, peers_sdk_1.PackageVersions)().get(activePackageVersionId);
51
- }, undefined, [activePackageVersionId]);
52
- if (!pv)
50
+ const all = await (0, peers_sdk_1.PackageVersions)().list({ packageId });
51
+ const active = all.find(v => v.packageVersionId === activePackageVersionId);
52
+ if (!active)
53
+ return null;
54
+ let newerLevel = null;
55
+ if (active.version) {
56
+ const parse = (v) => v.split('.').map(Number);
57
+ const [aMaj, aMin, aPat] = parse(active.version);
58
+ for (const v of all) {
59
+ if (!v.version || v.packageVersionId === activePackageVersionId)
60
+ continue;
61
+ if (!(0, peers_sdk_1.doesTagMatch)(active.versionTag, v.versionTag, followVersionTags, undefined))
62
+ continue;
63
+ const [maj, min, pat] = parse(v.version);
64
+ if (maj > aMaj) {
65
+ newerLevel = 'major';
66
+ break;
67
+ }
68
+ if (maj === aMaj && min > aMin) {
69
+ newerLevel = 'minor';
70
+ continue;
71
+ }
72
+ if (maj === aMaj && min === aMin && pat > aPat && !newerLevel) {
73
+ newerLevel = 'patch';
74
+ continue;
75
+ }
76
+ if (maj === aMaj && min === aMin && pat === aPat && (v.createdAt || '') > (active.createdAt || '') && !newerLevel) {
77
+ newerLevel = 'patch';
78
+ }
79
+ }
80
+ }
81
+ return { pv: active, newerLevel };
82
+ }, undefined, [activePackageVersionId, packageId, followVersionTags]);
83
+ if (!data)
53
84
  return null;
85
+ const { pv, newerLevel } = data;
54
86
  return (react_1.default.createElement("span", { className: "ms-2" },
55
87
  react_1.default.createElement("small", { className: "text-muted" },
56
88
  "v",
57
89
  pv.version),
58
- pv.versionTag && (react_1.default.createElement("span", { className: `badge ms-1 ${pv.versionTag.startsWith('beta') ? 'text-bg-warning' : 'text-bg-info'}`, style: { fontSize: '0.65em' } }, pv.versionTag))));
90
+ pv.versionTag && (react_1.default.createElement("span", { className: `badge ms-1 ${pv.versionTag.startsWith('beta') ? 'text-bg-warning' : 'text-bg-info'}`, style: { fontSize: '0.65em' } }, pv.versionTag)),
91
+ newerLevel ? (react_1.default.createElement("span", { className: `badge ms-1 text-bg-${newerLevel === 'major' ? 'danger' : newerLevel === 'minor' ? 'warning' : 'info'}`, style: { fontSize: '0.65em' } },
92
+ newerLevel,
93
+ " update")) : (react_1.default.createElement("span", { className: "badge ms-1 text-bg-success", style: { fontSize: '0.65em' } }, "up to date"))));
59
94
  };
60
95
  const PackageList = () => {
61
96
  const [searchTextObs] = (0, react_1.useState)(() => (0, peers_sdk_1.observable)(''));
@@ -171,7 +206,7 @@ const PackageList = () => {
171
206
  react_1.default.createElement("i", { className: "bi bi-box-fill" }),
172
207
  "\u00A0\u00A0",
173
208
  react_1.default.createElement("a", { href: `#packages/${pkg.packageId}` }, pkg.name),
174
- react_1.default.createElement(PackageVersionBadge, { activePackageVersionId: pkg.activePackageVersionId }),
209
+ react_1.default.createElement(PackageVersionBadge, { activePackageVersionId: pkg.activePackageVersionId, packageId: pkg.packageId, followVersionTags: pkg.followVersionTags }),
175
210
  react_1.default.createElement(tooltip_1.Tooltip, { markdownContent: pkg.description, positions: ['bottom', 'top', 'right', 'left'] })));
176
211
  });
177
212
  }, loadingIndicator: react_1.default.createElement("div", { className: "d-flex justify-content-center", style: { height: 200 } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peers-app/peers-ui",
3
- "version": "0.10.1",
3
+ "version": "0.10.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/peers-app/peers-ui.git"
@@ -26,7 +26,7 @@
26
26
  "test:coverage": "jest --coverage"
27
27
  },
28
28
  "peerDependencies": {
29
- "@peers-app/peers-sdk": "^0.10.1",
29
+ "@peers-app/peers-sdk": "^0.10.4",
30
30
  "bootstrap": "^5.3.3",
31
31
  "react": "^18.0.0",
32
32
  "react-dom": "^18.0.0"
@@ -37,7 +37,7 @@
37
37
  "@babel/preset-react": "^7.24.1",
38
38
  "@babel/preset-typescript": "^7.27.1",
39
39
  "@electron/rebuild": "^3.6.0",
40
- "@peers-app/peers-sdk": "0.10.1",
40
+ "@peers-app/peers-sdk": "0.10.4",
41
41
  "@testing-library/dom": "^10.4.0",
42
42
  "@testing-library/jest-dom": "^6.6.3",
43
43
  "@testing-library/react": "^16.3.0",
@@ -1,10 +1,10 @@
1
- import { Assistants, defaultAssistantId, getPrimaryAssistant, observable } from "@peers-app/peers-sdk";
2
- import React, { useState } from 'react';
1
+ import { Assistants, groupPrimaryAssistantVar, userPrimaryAssistantVar } from "@peers-app/peers-sdk";
2
+ import React from 'react';
3
3
  import { Input } from '../../components/input';
4
4
  import { LoadingIndicator } from '../../components/loading-indicator';
5
5
  import { SaveButton } from '../../components/save-button';
6
6
  import { Tabs } from '../../components/tabs';
7
- import { usePromise } from '../../hooks';
7
+ import { useObservable, usePromise } from '../../hooks';
8
8
  import { updateActiveTabTitle } from '../../tabs-layout/tabs-state';
9
9
  import { AssistantConfig } from './assistant-config';
10
10
  import { AssistantInfo } from './assistant-info';
@@ -13,16 +13,14 @@ import { AssistantTools } from './assistant-tools';
13
13
 
14
14
  export const AssistantDetails = (props: { assistantId: string }) => {
15
15
 
16
- const [isPrimaryAssistant] = useState(() => observable(false));
16
+ const [userAssistantId] = useObservable(userPrimaryAssistantVar);
17
+ const [groupAssistantId] = useObservable(groupPrimaryAssistantVar);
17
18
 
18
19
  const assistant = usePromise((async () => {
19
20
  const assistant = await Assistants().get(props.assistantId);
20
21
  if (!assistant) {
21
22
  return null;
22
- }
23
- const primaryAssistantId = (await getPrimaryAssistant()).assistantId
24
- || defaultAssistantId;
25
- isPrimaryAssistant(assistant.assistantId === primaryAssistantId);
23
+ }
26
24
  updateActiveTabTitle(assistant.name || "Assistant");
27
25
  return Assistants().initDoc(assistant);
28
26
  }), undefined, [props.assistantId]);
@@ -66,7 +64,22 @@ export const AssistantDetails = (props: { assistantId: string }) => {
66
64
  <Tabs
67
65
  key={assistant.assistantId}
68
66
  tabs={[
69
- { name: 'Info', content: <AssistantInfo assistant={assistant} isPrimaryAssistant={isPrimaryAssistant} /> },
67
+ {
68
+ name: 'Info',
69
+ content: (
70
+ <AssistantInfo
71
+ assistant={assistant}
72
+ isPrimaryAssistant={userAssistantId === assistant.assistantId}
73
+ isGroupPrimaryAssistant={groupAssistantId === assistant.assistantId}
74
+ onSetPrimary={(checked) =>
75
+ userPrimaryAssistantVar(checked ? assistant.assistantId : undefined)
76
+ }
77
+ onSetGroupPrimary={(checked) =>
78
+ groupPrimaryAssistantVar(checked ? assistant.assistantId : undefined)
79
+ }
80
+ />
81
+ ),
82
+ },
70
83
  { name: 'Config', content: <AssistantConfig assistant={assistant} /> },
71
84
  { name: 'Tools', content: <AssistantTools assistant={assistant} />, },
72
85
  ]}
@@ -1,13 +1,18 @@
1
- import { IAssistant, IDoc, Observable } from "@peers-app/peers-sdk";
1
+ import { IAssistant, IDoc } from "@peers-app/peers-sdk";
2
2
  import React from "react";
3
3
  import { Input } from "../../components/input";
4
4
  import { MarkdownEditorInline } from "../../components/markdown-editor/editor-inline";
5
- import { useObservable } from "../../hooks";
6
5
 
7
- export const AssistantInfo = (props: { assistant: IDoc<IAssistant>, isPrimaryAssistant: Observable<boolean> }) => {
8
- const { assistant, isPrimaryAssistant } = props;
6
+ interface AssistantInfoProps {
7
+ assistant: IDoc<IAssistant>;
8
+ isPrimaryAssistant: boolean;
9
+ isGroupPrimaryAssistant: boolean;
10
+ onSetPrimary: (checked: boolean) => void;
11
+ onSetGroupPrimary: (checked: boolean) => void;
12
+ }
9
13
 
10
- useObservable(isPrimaryAssistant);
14
+ export const AssistantInfo = (props: AssistantInfoProps) => {
15
+ const { assistant, isPrimaryAssistant, isGroupPrimaryAssistant, onSetPrimary, onSetGroupPrimary } = props;
11
16
 
12
17
  return (
13
18
  <div>
@@ -21,15 +26,22 @@ export const AssistantInfo = (props: { assistant: IDoc<IAssistant>, isPrimaryAss
21
26
  />
22
27
 
23
28
  <div>
24
- <label htmlFor="isPrimary" className="form-label small mt-2 me-2">Primary Assistant:</label>
29
+ <label htmlFor="isPrimary" className="form-label small mt-2 me-2">My Default Assistant:</label>
25
30
  <input
26
31
  type="checkbox"
27
- checked={isPrimaryAssistant()}
28
- onChange={(e) => {
29
- const checked = (e.target as HTMLInputElement).checked;
30
- isPrimaryAssistant(checked);
31
- assistant.q(assistant.q() + 1);
32
- }}
32
+ id="isPrimary"
33
+ checked={isPrimaryAssistant}
34
+ onChange={(e) => onSetPrimary((e.target as HTMLInputElement).checked)}
35
+ />
36
+ </div>
37
+
38
+ <div>
39
+ <label htmlFor="isGroupPrimary" className="form-label small mt-2 me-2">Group Default Assistant:</label>
40
+ <input
41
+ type="checkbox"
42
+ id="isGroupPrimary"
43
+ checked={isGroupPrimaryAssistant}
44
+ onChange={(e) => onSetGroupPrimary((e.target as HTMLInputElement).checked)}
33
45
  />
34
46
  </div>
35
47
 
@@ -42,4 +54,4 @@ export const AssistantInfo = (props: { assistant: IDoc<IAssistant>, isPrimaryAss
42
54
 
43
55
  </div>
44
56
  );
45
- };
57
+ };
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { rpcServerCalls, IDoc, IPackage, IPackageVersion, PackageVersions, packagesRootDir, groupDeviceVar } from "@peers-app/peers-sdk";
2
+ import { rpcServerCalls, IDoc, IPackage, IPackageVersion, PackageVersions, packagesRootDir, groupDeviceVar, doesTagMatch } from "@peers-app/peers-sdk";
3
3
  import { MarkdownWithMentions } from "../../components/markdown-with-mentions";
4
4
  import { Tooltip } from "../../components/tooltip";
5
5
  import { Input } from "../../components/input";
@@ -70,6 +70,7 @@ export const PackageInfo = (props: {
70
70
  let highest: 'major' | 'minor' | 'patch' | null = null;
71
71
  for (const v of all) {
72
72
  if (!v.version || v.packageVersionId === activeVersionId) continue;
73
+ if (!doesTagMatch(active.versionTag, v.versionTag, followVersionTags, deviceTag)) continue;
73
74
  const [maj, min, pat] = parse(v.version);
74
75
  if (maj > aMaj) return 'major';
75
76
  if (maj === aMaj && min > aMin) { highest = 'minor'; continue; }
@@ -77,7 +78,7 @@ export const PackageInfo = (props: {
77
78
  if (maj === aMaj && min === aMin && pat === aPat && (v.createdAt || '') > (active.createdAt || '') && !highest) { highest = 'patch'; }
78
79
  }
79
80
  return highest || 'uptodate';
80
- }, undefined, [activeVersionId, pkg.packageId]);
81
+ }, undefined, [activeVersionId, pkg.packageId, followVersionTags, deviceTag]);
81
82
 
82
83
  const isPinned = versionFollowRange === 'pinned';
83
84
 
@@ -134,17 +135,6 @@ export const PackageInfo = (props: {
134
135
  </div>
135
136
  )}
136
137
 
137
- <div className="mt-2">
138
- <hr />
139
- <small>
140
- Description:
141
- <Tooltip markdownContent={`This should be edited in the package's README.md. It will automatically update when you restart Peers or reload the package.`} />
142
- </small>
143
- <MarkdownWithMentions
144
- content={pkg.description || ''}
145
- />
146
- </div>
147
-
148
138
  <div className="mt-2">
149
139
  <hr />
150
140
  <small>
@@ -231,6 +221,17 @@ export const PackageInfo = (props: {
231
221
  </div>
232
222
  </div>
233
223
 
224
+ <div className="mt-2">
225
+ <hr />
226
+ <small>
227
+ Description:
228
+ <Tooltip markdownContent={`This should be edited in the package's README.md. It will automatically update when you restart Peers or reload the package.`} />
229
+ </small>
230
+ <MarkdownWithMentions
231
+ content={pkg.description || ''}
232
+ />
233
+ </div>
234
+
234
235
  </div>
235
236
  )
236
237
  }
@@ -1,4 +1,4 @@
1
- import { ICursorIterable, IPackage, observable, Packages, PackageVersions, packagesRootDir, rpcServerCalls } from "@peers-app/peers-sdk";
1
+ import { ICursorIterable, IPackage, observable, Packages, PackageVersions, packagesRootDir, rpcServerCalls, doesTagMatch } from "@peers-app/peers-sdk";
2
2
  import React, { useEffect, useState } from 'react';
3
3
  import { Input } from "../../components/input";
4
4
  import { LazyList } from "../../components/lazy-list";
@@ -8,13 +8,32 @@ import { isDesktop, mainContentPath } from '../../globals';
8
8
  import { useObservable, useObservableState, usePromise } from "../../hooks";
9
9
  import { registerInternalPeersUI } from "../../ui-router/ui-loader";
10
10
 
11
- const PackageVersionBadge = ({ activePackageVersionId }: { activePackageVersionId?: string }) => {
12
- const pv = usePromise(async () => {
11
+ const PackageVersionBadge = ({ activePackageVersionId, packageId, followVersionTags }: { activePackageVersionId?: string, packageId: string, followVersionTags?: string }) => {
12
+ const data = usePromise(async () => {
13
13
  if (!activePackageVersionId) return null;
14
- return PackageVersions().get(activePackageVersionId);
15
- }, undefined, [activePackageVersionId]);
14
+ const all = await PackageVersions().list({ packageId });
15
+ const active = all.find(v => v.packageVersionId === activePackageVersionId);
16
+ if (!active) return null;
17
+
18
+ let newerLevel: 'major' | 'minor' | 'patch' | null = null;
19
+ if (active.version) {
20
+ const parse = (v: string) => v.split('.').map(Number);
21
+ const [aMaj, aMin, aPat] = parse(active.version);
22
+ for (const v of all) {
23
+ if (!v.version || v.packageVersionId === activePackageVersionId) continue;
24
+ if (!doesTagMatch(active.versionTag, v.versionTag, followVersionTags, undefined)) continue;
25
+ const [maj, min, pat] = parse(v.version);
26
+ if (maj > aMaj) { newerLevel = 'major'; break; }
27
+ if (maj === aMaj && min > aMin) { newerLevel = 'minor'; continue; }
28
+ if (maj === aMaj && min === aMin && pat > aPat && !newerLevel) { newerLevel = 'patch'; continue; }
29
+ if (maj === aMaj && min === aMin && pat === aPat && (v.createdAt || '') > (active.createdAt || '') && !newerLevel) { newerLevel = 'patch'; }
30
+ }
31
+ }
32
+ return { pv: active, newerLevel };
33
+ }, undefined, [activePackageVersionId, packageId, followVersionTags]);
16
34
 
17
- if (!pv) return null;
35
+ if (!data) return null;
36
+ const { pv, newerLevel } = data;
18
37
 
19
38
  return (
20
39
  <span className="ms-2">
@@ -26,6 +45,15 @@ const PackageVersionBadge = ({ activePackageVersionId }: { activePackageVersionI
26
45
  {pv.versionTag}
27
46
  </span>
28
47
  )}
48
+ {newerLevel ? (
49
+ <span className={`badge ms-1 text-bg-${newerLevel === 'major' ? 'danger' : newerLevel === 'minor' ? 'warning' : 'info'}`}
50
+ style={{ fontSize: '0.65em' }}
51
+ >
52
+ {newerLevel} update
53
+ </span>
54
+ ) : (
55
+ <span className="badge ms-1 text-bg-success" style={{ fontSize: '0.65em' }}>up to date</span>
56
+ )}
29
57
  </span>
30
58
  );
31
59
  };
@@ -168,7 +196,7 @@ export const PackageList = () => {
168
196
  <a href={`#packages/${pkg.packageId}`}>
169
197
  {pkg.name}
170
198
  </a>
171
- <PackageVersionBadge activePackageVersionId={pkg.activePackageVersionId} />
199
+ <PackageVersionBadge activePackageVersionId={pkg.activePackageVersionId} packageId={pkg.packageId} followVersionTags={pkg.followVersionTags} />
172
200
  <Tooltip
173
201
  markdownContent={pkg.description}
174
202
  positions={['bottom', 'top', 'right', 'left']}