@frockbot/plugin-settings 0.3.6 → 0.3.8
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/package.json +10 -10
- package/src/client/ConnectionsSurface.vue +59 -0
- package/src/client/PluginsSurface.vue +76 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@frockbot/plugin-settings",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -21,15 +21,15 @@
|
|
|
21
21
|
"typecheck": "vue-tsc --noEmit -p tsconfig.json"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@frockbot/catalog-core": "0.3.
|
|
25
|
-
"@frockbot/client-core": "0.3.
|
|
26
|
-
"@frockbot/client-ui": "0.3.
|
|
27
|
-
"@frockbot/configuration-core": "0.3.
|
|
28
|
-
"@frockbot/connection-core": "0.3.
|
|
29
|
-
"@frockbot/kernel-composition": "0.3.
|
|
30
|
-
"@frockbot/kernel-contracts": "0.3.
|
|
31
|
-
"@frockbot/plugin-auth": "0.3.
|
|
32
|
-
"@frockbot/plugin-shell": "0.3.
|
|
24
|
+
"@frockbot/catalog-core": "0.3.8",
|
|
25
|
+
"@frockbot/client-core": "0.3.8",
|
|
26
|
+
"@frockbot/client-ui": "0.3.8",
|
|
27
|
+
"@frockbot/configuration-core": "0.3.8",
|
|
28
|
+
"@frockbot/connection-core": "0.3.8",
|
|
29
|
+
"@frockbot/kernel-composition": "0.3.8",
|
|
30
|
+
"@frockbot/kernel-contracts": "0.3.8",
|
|
31
|
+
"@frockbot/plugin-auth": "0.3.8",
|
|
32
|
+
"@frockbot/plugin-shell": "0.3.8",
|
|
33
33
|
"cordis": "4.0.0-rc.8",
|
|
34
34
|
"vue": "3.5.41"
|
|
35
35
|
},
|
|
@@ -28,6 +28,38 @@ const web = providedWeb;
|
|
|
28
28
|
// Connections are User-scoped, so the row's link names no Bot.
|
|
29
29
|
const connectionsLink = settingsLinkV1({ anchor: "user-connections" });
|
|
30
30
|
|
|
31
|
+
/**
|
|
32
|
+
* What the browser came back from an external grant with. Read at boot from the
|
|
33
|
+
* return URL; without this the User lands back in the app with no confirmation
|
|
34
|
+
* and a cancelled grant is silently discarded.
|
|
35
|
+
*/
|
|
36
|
+
const connectionReturn = computed(() => web.value.connectionReturn);
|
|
37
|
+
|
|
38
|
+
const connectionReturnMessage = computed(() => {
|
|
39
|
+
const result = connectionReturn.value;
|
|
40
|
+
if (!result) return "";
|
|
41
|
+
const name = connectorDisplayName(result.packageId);
|
|
42
|
+
if (result.status === "ready") return `${name} is connected.`;
|
|
43
|
+
if (result.status === "pending") {
|
|
44
|
+
return `${name} is finishing connecting. This page will show it when it is ready.`;
|
|
45
|
+
}
|
|
46
|
+
return result.reason
|
|
47
|
+
? `${name} did not connect: ${result.reason}`
|
|
48
|
+
: `${name} did not connect.`;
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** The Package's display name when the catalog knows it, its id otherwise. */
|
|
52
|
+
function connectorDisplayName(packageId: string): string {
|
|
53
|
+
return (
|
|
54
|
+
web.value.pluginCatalog.find((item) => item.packageId === packageId)
|
|
55
|
+
?.displayName ?? packageId
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function dismissConnectionReturn(): void {
|
|
60
|
+
web.value.connectionReturn = undefined;
|
|
61
|
+
}
|
|
62
|
+
|
|
31
63
|
const connectors = computed(() =>
|
|
32
64
|
configurablePackages({
|
|
33
65
|
catalog: web.value.pluginCatalog,
|
|
@@ -331,6 +363,18 @@ async function connect(
|
|
|
331
363
|
Connect an account or service once and every Bot you own can use it.
|
|
332
364
|
</p>
|
|
333
365
|
|
|
366
|
+
<p
|
|
367
|
+
v-if="connectionReturn"
|
|
368
|
+
class="connection-return"
|
|
369
|
+
:class="{
|
|
370
|
+
'connection-return--failed': connectionReturn.status === 'failed',
|
|
371
|
+
}"
|
|
372
|
+
role="status"
|
|
373
|
+
>
|
|
374
|
+
{{ connectionReturnMessage }}
|
|
375
|
+
<UiButton @click="dismissConnectionReturn">Dismiss</UiButton>
|
|
376
|
+
</p>
|
|
377
|
+
|
|
334
378
|
<section
|
|
335
379
|
v-if="pendingAuthorizations.length"
|
|
336
380
|
class="connect-cards"
|
|
@@ -722,6 +766,21 @@ async function connect(
|
|
|
722
766
|
font-size: var(--frock-text-sm);
|
|
723
767
|
}
|
|
724
768
|
|
|
769
|
+
.connection-return {
|
|
770
|
+
display: flex;
|
|
771
|
+
align-items: center;
|
|
772
|
+
justify-content: space-between;
|
|
773
|
+
gap: 12px;
|
|
774
|
+
margin: 8px 0;
|
|
775
|
+
padding: 8px 12px;
|
|
776
|
+
border-radius: var(--frock-radius-card);
|
|
777
|
+
background: var(--frock-surface-accent);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
.connection-return--failed {
|
|
781
|
+
background: var(--frock-surface-danger, var(--frock-surface-accent));
|
|
782
|
+
}
|
|
783
|
+
|
|
725
784
|
.connections-empty {
|
|
726
785
|
display: flex;
|
|
727
786
|
flex-direction: column;
|
|
@@ -78,7 +78,10 @@ function capabilityNoun(kind: string): string {
|
|
|
78
78
|
/** What a plugin offers, in plain words. */
|
|
79
79
|
function capabilitySummary(item: PluginCatalogItem): string {
|
|
80
80
|
const kinds = [...new Set(item.capabilities.map((entry) => entry.kind))];
|
|
81
|
-
|
|
81
|
+
// A plugin can be worth turning on without contributing a capability of its
|
|
82
|
+
// own — Custom models is exactly that — so saying "no features" tells the
|
|
83
|
+
// User the one plugin they must enable does nothing.
|
|
84
|
+
if (kinds.length === 0) return "Adds settings";
|
|
82
85
|
return kinds.map(capabilityNoun).join(", ");
|
|
83
86
|
}
|
|
84
87
|
|
|
@@ -126,13 +129,71 @@ function openPackageCatalog(): void {
|
|
|
126
129
|
surfaces.open("package-catalog");
|
|
127
130
|
}
|
|
128
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Refusals live on the row that was clicked, not in one panel-wide slot.
|
|
134
|
+
*
|
|
135
|
+
* The grid scrolls, so a message rendered above the list is off-screen for
|
|
136
|
+
* every row past the fold: the User sees the row unchanged and no explanation
|
|
137
|
+
* anywhere.
|
|
138
|
+
*/
|
|
139
|
+
const rowErrors = ref<Record<string, string>>({});
|
|
140
|
+
|
|
141
|
+
function rowError(packageId: string): string | undefined {
|
|
142
|
+
return rowErrors.value[packageId];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function setRowError(packageId: string, message: string): void {
|
|
146
|
+
rowErrors.value = { ...rowErrors.value, [packageId]: message };
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function clearRowError(packageId: string): void {
|
|
150
|
+
const { [packageId]: _cleared, ...rest } = rowErrors.value;
|
|
151
|
+
rowErrors.value = rest;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Say what a Package is called, not what its id is.
|
|
156
|
+
*
|
|
157
|
+
* The backend refuses in terms of ids because that is what it holds; the
|
|
158
|
+
* surface knows the display names, so it substitutes them before the User
|
|
159
|
+
* reads the message.
|
|
160
|
+
*/
|
|
161
|
+
function inPlainNames(message: string): string {
|
|
162
|
+
const dependency = message.match(
|
|
163
|
+
/^Package "([^"]+)" requires Package "([^"]+)" to be installed and enabled/,
|
|
164
|
+
);
|
|
165
|
+
if (dependency) {
|
|
166
|
+
return `${displayName(dependency[1]!)} needs ${displayName(
|
|
167
|
+
dependency[2]!,
|
|
168
|
+
)} turned on first.`;
|
|
169
|
+
}
|
|
170
|
+
return message.replace(/"([a-z0-9][a-z0-9-]*)"/g, (quoted, packageId) =>
|
|
171
|
+
displayName(packageId) === packageId
|
|
172
|
+
? quoted
|
|
173
|
+
: `"${displayName(packageId)}"`,
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function displayName(packageId: string): string {
|
|
178
|
+
return (
|
|
179
|
+
web.value.pluginCatalog.find(
|
|
180
|
+
(candidate) => candidate.packageId === packageId,
|
|
181
|
+
)?.displayName ?? packageId
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
129
185
|
async function install(item: PluginCatalogItem): Promise<void> {
|
|
130
186
|
busyPackageId.value = item.packageId;
|
|
187
|
+
clearRowError(item.packageId);
|
|
131
188
|
try {
|
|
132
189
|
await web.value.installPackage(item.packageId, item.version);
|
|
133
190
|
} catch (error) {
|
|
134
|
-
|
|
135
|
-
|
|
191
|
+
setRowError(
|
|
192
|
+
item.packageId,
|
|
193
|
+
inPlainNames(
|
|
194
|
+
error instanceof Error ? error.message : "Couldn't add that plugin.",
|
|
195
|
+
),
|
|
196
|
+
);
|
|
136
197
|
} finally {
|
|
137
198
|
busyPackageId.value = undefined;
|
|
138
199
|
}
|
|
@@ -140,13 +201,18 @@ async function install(item: PluginCatalogItem): Promise<void> {
|
|
|
140
201
|
|
|
141
202
|
async function setEnabled(packageId: string, next: boolean): Promise<void> {
|
|
142
203
|
busyPackageId.value = packageId;
|
|
204
|
+
clearRowError(packageId);
|
|
143
205
|
try {
|
|
144
206
|
await web.value.setPackageEnabled(packageId, next);
|
|
145
207
|
} catch (error) {
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
208
|
+
setRowError(
|
|
209
|
+
packageId,
|
|
210
|
+
inPlainNames(
|
|
211
|
+
error instanceof Error
|
|
212
|
+
? error.message
|
|
213
|
+
: "Couldn't turn that plugin on or off.",
|
|
214
|
+
),
|
|
215
|
+
);
|
|
150
216
|
} finally {
|
|
151
217
|
busyPackageId.value = undefined;
|
|
152
218
|
}
|
|
@@ -161,14 +227,6 @@ async function setEnabled(packageId: string, next: boolean): Promise<void> {
|
|
|
161
227
|
</span>
|
|
162
228
|
{{ installedPluginCount }} installed
|
|
163
229
|
</div>
|
|
164
|
-
<!--
|
|
165
|
-
A refused command is the answer to the click that caused it, so it is
|
|
166
|
-
shown above the list where the click happened, not below a long grid
|
|
167
|
-
where it scrolls out of view.
|
|
168
|
-
-->
|
|
169
|
-
<p v-if="web.settingsError" class="settings-error" role="alert">
|
|
170
|
-
{{ web.settingsError }}
|
|
171
|
-
</p>
|
|
172
230
|
<label class="plugin-search">
|
|
173
231
|
<UiIcon name="search" />
|
|
174
232
|
<input
|
|
@@ -242,6 +300,9 @@ async function setEnabled(packageId: string, next: boolean): Promise<void> {
|
|
|
242
300
|
</UiButton>
|
|
243
301
|
</span>
|
|
244
302
|
</div>
|
|
303
|
+
<p v-if="rowError(item.packageId)" class="plugin-failure" role="alert">
|
|
304
|
+
{{ rowError(item.packageId) }}
|
|
305
|
+
</p>
|
|
245
306
|
<p v-if="failure(item.packageId)" class="plugin-failure" role="alert">
|
|
246
307
|
{{ failure(item.packageId) }}
|
|
247
308
|
</p>
|