@frockbot/plugin-settings 0.1.3 → 0.1.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.
- package/frockbot.json +1 -0
- package/package.json +9 -9
- package/src/client/BotSettingsSurface.vue +22 -575
- package/src/client/ConnectionsSurface.vue +59 -10
- package/src/client/{PluginsTrigger.vue → ConnectorsTrigger.vue} +2 -2
- package/src/client/ModelsSurface.vue +26 -364
- package/src/client/PackageAccounts.vue +2 -25
- package/src/client/PackageCatalogSurface.vue +3 -3
- package/src/client/PackageSettingsForm.vue +6 -1
- package/src/client/PluginsSurface.vue +1 -1
- package/src/client/UserProfileTrigger.vue +2 -2
- package/src/client/UserSettingsSurface.vue +4 -18
- package/src/client/bot-settings.test.ts +71 -160
- package/src/client/bot-settings.ts +5 -42
- package/src/client/index.test.ts +7 -41
- package/src/client/index.ts +15 -5
- package/src/client/package-surfaces.test.ts +29 -3
- package/src/client/package-surfaces.ts +12 -3
- package/src/user.test.ts +270 -316
- package/src/user.ts +112 -344
- package/src/client/assignment-operations.ts +0 -22
|
@@ -13,24 +13,12 @@ import { settingsLinkV1 } from "@frockbot/plugin-shell/settings-links";
|
|
|
13
13
|
import {
|
|
14
14
|
computed,
|
|
15
15
|
inject,
|
|
16
|
+
nextTick,
|
|
16
17
|
onBeforeUnmount,
|
|
17
18
|
onMounted,
|
|
18
|
-
reactive,
|
|
19
19
|
ref,
|
|
20
|
-
nextTick,
|
|
21
20
|
watch,
|
|
22
21
|
} from "vue";
|
|
23
|
-
import {
|
|
24
|
-
assignmentHasPendingOperation,
|
|
25
|
-
projectAssignmentOperations,
|
|
26
|
-
} from "./assignment-operations.js";
|
|
27
|
-
import {
|
|
28
|
-
describeModelAssignment,
|
|
29
|
-
eligibleModelConnections,
|
|
30
|
-
encodeModelSelection,
|
|
31
|
-
modelSelectOptions,
|
|
32
|
-
resolveBotSettingsModel,
|
|
33
|
-
} from "./bot-settings.js";
|
|
34
22
|
|
|
35
23
|
const providedSurfaces = inject(clientSurfaceRegistryKey);
|
|
36
24
|
const providedWeb = inject(frockBotWebDataKey);
|
|
@@ -40,26 +28,13 @@ if (!providedSurfaces || !providedWeb) {
|
|
|
40
28
|
const surfaces = providedSurfaces;
|
|
41
29
|
const web = providedWeb;
|
|
42
30
|
|
|
43
|
-
/*
|
|
44
|
-
* Every row below is deep-linkable. The scheme and the anchor table live in
|
|
45
|
-
* `@frockbot/plugin-shell/settings-links`, so a Bot citing a row and the panel
|
|
46
|
-
* rendering it read the same list — a link nobody registered does not resolve,
|
|
47
|
-
* and a row nobody linked has no link to copy.
|
|
48
|
-
*/
|
|
49
31
|
function link(anchor: string): string {
|
|
50
32
|
return settingsLinkV1({ anchor, botId: web.value.activeBotId });
|
|
51
33
|
}
|
|
52
34
|
|
|
53
|
-
/*
|
|
54
|
-
* Model and Assignments live under the Advanced disclosure, and a collapsed
|
|
55
|
-
* `details` cannot be scrolled to. A link into either opens it first; the User
|
|
56
|
-
* can still close it, and the `toggle` handler keeps their choice.
|
|
57
|
-
*/
|
|
58
35
|
const ADVANCED_ANCHORS = new Set([
|
|
59
36
|
"bot-title",
|
|
60
37
|
"bot-hidden-from-sidebar",
|
|
61
|
-
"bot-model",
|
|
62
|
-
"bot-capabilities",
|
|
63
38
|
"bot-routines",
|
|
64
39
|
"bot-audit",
|
|
65
40
|
"bot-info-identity",
|
|
@@ -85,15 +60,11 @@ const description = ref("");
|
|
|
85
60
|
const title = ref("");
|
|
86
61
|
const hiddenFromSidebar = ref(false);
|
|
87
62
|
const notifications = ref(false);
|
|
88
|
-
/**
|
|
89
|
-
* The Bot's undecided approval cards. Read from the same backend state the
|
|
90
|
-
* conversation renders, so the two surfaces cannot disagree about what is
|
|
91
|
-
* still waiting.
|
|
92
|
-
*/
|
|
93
63
|
const pendingApprovals = computed(() =>
|
|
94
64
|
web.value.approvals.filter((approval) => approval.decision === "pending"),
|
|
95
65
|
);
|
|
96
66
|
const decidingApproval = ref<string>();
|
|
67
|
+
const saving = ref(false);
|
|
97
68
|
|
|
98
69
|
async function decideApproval(
|
|
99
70
|
approvalId: string,
|
|
@@ -106,91 +77,6 @@ async function decideApproval(
|
|
|
106
77
|
decidingApproval.value = undefined;
|
|
107
78
|
}
|
|
108
79
|
}
|
|
109
|
-
const saving = ref(false);
|
|
110
|
-
const modelMode = ref<"default" | "custom">("default");
|
|
111
|
-
const assignmentBusy = ref<string>();
|
|
112
|
-
const selectedConnections = reactive<Record<string, string>>({});
|
|
113
|
-
|
|
114
|
-
const capabilityItems = computed(() =>
|
|
115
|
-
web.value.pluginCatalog.flatMap((pkg) =>
|
|
116
|
-
web.value.userSettings?.packages.some(
|
|
117
|
-
(installation) =>
|
|
118
|
-
installation.packageId === pkg.packageId &&
|
|
119
|
-
installation.version === pkg.version &&
|
|
120
|
-
installation.state === "installed",
|
|
121
|
-
)
|
|
122
|
-
? pkg.capabilities.map((capability) => {
|
|
123
|
-
const existing = web.value.botSettings?.assignments.find(
|
|
124
|
-
(assignment) =>
|
|
125
|
-
assignment.packageId === pkg.packageId &&
|
|
126
|
-
assignment.capabilityId === capability.id,
|
|
127
|
-
);
|
|
128
|
-
const pending = web.value.botSettings?.assignmentOperations.find(
|
|
129
|
-
(operation) =>
|
|
130
|
-
operation.assignmentId === existing?.assignmentId ||
|
|
131
|
-
(operation.target?.packageId === pkg.packageId &&
|
|
132
|
-
operation.target.capabilityId === capability.id),
|
|
133
|
-
);
|
|
134
|
-
const connections =
|
|
135
|
-
web.value.userSettings?.connections.filter(
|
|
136
|
-
(connection) =>
|
|
137
|
-
connection.packageId === pkg.packageId &&
|
|
138
|
-
connection.state === "ready" &&
|
|
139
|
-
capability.connectionTypes.includes(
|
|
140
|
-
connection.connectionTypeId,
|
|
141
|
-
),
|
|
142
|
-
) ?? [];
|
|
143
|
-
const key = `${pkg.packageId}:${capability.id}`;
|
|
144
|
-
if (!(key in selectedConnections) && existing?.connectionId) {
|
|
145
|
-
selectedConnections[key] = existing.connectionId;
|
|
146
|
-
}
|
|
147
|
-
return { key, pkg, capability, existing, pending, connections };
|
|
148
|
-
})
|
|
149
|
-
: [],
|
|
150
|
-
),
|
|
151
|
-
);
|
|
152
|
-
|
|
153
|
-
const assignmentOperations = computed(() =>
|
|
154
|
-
projectAssignmentOperations(web.value.botSettings),
|
|
155
|
-
);
|
|
156
|
-
|
|
157
|
-
const orphanAssignments = computed(
|
|
158
|
-
() =>
|
|
159
|
-
web.value.botSettings?.assignments.filter(
|
|
160
|
-
(assignment) =>
|
|
161
|
-
!capabilityItems.value.some(
|
|
162
|
-
(item) => item.existing?.assignmentId === assignment.assignmentId,
|
|
163
|
-
),
|
|
164
|
-
) ?? [],
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
function assignmentOperationPending(assignmentId: string): boolean {
|
|
168
|
-
return assignmentHasPendingOperation(
|
|
169
|
-
assignmentOperations.value,
|
|
170
|
-
assignmentId,
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
const selectedModel = ref("");
|
|
175
|
-
const useExactModel = ref(false);
|
|
176
|
-
const exactConnectionId = ref("");
|
|
177
|
-
const exactProviderModelId = ref("");
|
|
178
|
-
const readyConnections = computed(() =>
|
|
179
|
-
eligibleModelConnections({
|
|
180
|
-
connections: web.value.userSettings?.connections ?? [],
|
|
181
|
-
packages: web.value.userSettings?.packages ?? [],
|
|
182
|
-
catalog: web.value.pluginCatalog,
|
|
183
|
-
}),
|
|
184
|
-
);
|
|
185
|
-
const modelOptions = computed(() => modelSelectOptions(readyConnections.value));
|
|
186
|
-
const defaultModelName = computed(
|
|
187
|
-
() =>
|
|
188
|
-
describeModelAssignment(
|
|
189
|
-
web.value.userSettings?.newBotModelTemplate,
|
|
190
|
-
web.value.userSettings?.connections ?? [],
|
|
191
|
-
) ?? "none set",
|
|
192
|
-
);
|
|
193
|
-
const overriding = computed(() => Boolean(web.value.botSettings?.model));
|
|
194
80
|
|
|
195
81
|
onMounted(() => {
|
|
196
82
|
window.addEventListener(UI_ANCHOR_EVENT, onAnchorAnnounced);
|
|
@@ -200,13 +86,6 @@ onMounted(() => {
|
|
|
200
86
|
void web.value.loadUserSettings();
|
|
201
87
|
});
|
|
202
88
|
|
|
203
|
-
/*
|
|
204
|
-
* The form fills itself from whichever Bot's durable settings arrive, rather
|
|
205
|
-
* than from whatever had loaded by the time this panel mounted. A deep link
|
|
206
|
-
* opens the panel before the Flock has selected a Bot, and a User can switch
|
|
207
|
-
* Bots with the panel open; both used to leave the fields on screen belonging
|
|
208
|
-
* to nobody.
|
|
209
|
-
*/
|
|
210
89
|
const hydratedBotId = ref<string>();
|
|
211
90
|
|
|
212
91
|
watch(
|
|
@@ -220,53 +99,17 @@ watch(
|
|
|
220
99
|
title.value = settings.profile.title ?? "";
|
|
221
100
|
hiddenFromSidebar.value = settings.profile.hiddenFromSidebar === true;
|
|
222
101
|
notifications.value = settings.notifications.enabled;
|
|
223
|
-
modelMode.value = settings.model ? "custom" : "default";
|
|
224
|
-
selectedModel.value = encodeModelSelection(settings.model);
|
|
225
|
-
exactConnectionId.value =
|
|
226
|
-
settings.model?.connectionId ??
|
|
227
|
-
readyConnections.value[0]?.connectionId ??
|
|
228
|
-
"";
|
|
229
|
-
exactProviderModelId.value = settings.model?.providerModelId ?? "";
|
|
230
|
-
useExactModel.value = Boolean(
|
|
231
|
-
settings.model &&
|
|
232
|
-
!modelOptions.value.some((model) => model.value === selectedModel.value),
|
|
233
|
-
);
|
|
234
102
|
},
|
|
235
103
|
{ immediate: true },
|
|
236
104
|
);
|
|
237
105
|
|
|
238
|
-
// The Connections may land after the Bot did; an empty exact-model Connection
|
|
239
|
-
// takes the first ready one the moment there is one.
|
|
240
|
-
watch(readyConnections, (connections) => {
|
|
241
|
-
if (!exactConnectionId.value && connections[0]) {
|
|
242
|
-
exactConnectionId.value = connections[0].connectionId;
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
106
|
onBeforeUnmount(() =>
|
|
247
107
|
window.removeEventListener(UI_ANCHOR_EVENT, onAnchorAnnounced),
|
|
248
108
|
);
|
|
249
109
|
|
|
250
|
-
async function saveModel(): Promise<void> {
|
|
251
|
-
if (modelMode.value === "default") {
|
|
252
|
-
// Following the default means holding no Bot binding at all.
|
|
253
|
-
if (web.value.botSettings?.model) await web.value.clearBotModel();
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
const selected = resolveBotSettingsModel({
|
|
257
|
-
current: web.value.botSettings?.model,
|
|
258
|
-
useExactModel: useExactModel.value,
|
|
259
|
-
selectedModel: selectedModel.value,
|
|
260
|
-
exactConnectionId: exactConnectionId.value,
|
|
261
|
-
exactProviderModelId: exactProviderModelId.value,
|
|
262
|
-
});
|
|
263
|
-
if (selected) await web.value.saveBotModel(selected);
|
|
264
|
-
}
|
|
265
|
-
|
|
266
110
|
async function save(): Promise<void> {
|
|
267
111
|
saving.value = true;
|
|
268
112
|
try {
|
|
269
|
-
// A partial update: the empty string clears an optional field.
|
|
270
113
|
await web.value.setBotProfile({
|
|
271
114
|
name: name.value,
|
|
272
115
|
label: label.value,
|
|
@@ -274,7 +117,6 @@ async function save(): Promise<void> {
|
|
|
274
117
|
title: title.value,
|
|
275
118
|
hiddenFromSidebar: hiddenFromSidebar.value,
|
|
276
119
|
});
|
|
277
|
-
await saveModel();
|
|
278
120
|
await web.value.saveBotNotifications({ enabled: notifications.value });
|
|
279
121
|
surfaces.close();
|
|
280
122
|
} catch (error) {
|
|
@@ -284,70 +126,6 @@ async function save(): Promise<void> {
|
|
|
284
126
|
saving.value = false;
|
|
285
127
|
}
|
|
286
128
|
}
|
|
287
|
-
|
|
288
|
-
async function assign(
|
|
289
|
-
item: (typeof capabilityItems.value)[number],
|
|
290
|
-
): Promise<void> {
|
|
291
|
-
assignmentBusy.value = item.key;
|
|
292
|
-
try {
|
|
293
|
-
await web.value.assignCapability({
|
|
294
|
-
assignmentId: crypto.randomUUID(),
|
|
295
|
-
packageId: item.pkg.packageId,
|
|
296
|
-
capabilityId: item.capability.id,
|
|
297
|
-
connectionId: selectedConnections[item.key] || undefined,
|
|
298
|
-
});
|
|
299
|
-
} catch (error) {
|
|
300
|
-
web.value.settingsError =
|
|
301
|
-
error instanceof Error ? error.message : "Could not assign Capability";
|
|
302
|
-
await web.value.loadBotSettings();
|
|
303
|
-
} finally {
|
|
304
|
-
assignmentBusy.value = undefined;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
async function replace(
|
|
309
|
-
item: (typeof capabilityItems.value)[number],
|
|
310
|
-
): Promise<void> {
|
|
311
|
-
if (!item.existing) return;
|
|
312
|
-
assignmentBusy.value = item.key;
|
|
313
|
-
try {
|
|
314
|
-
await web.value.replaceCapability({
|
|
315
|
-
assignmentId: item.existing.assignmentId,
|
|
316
|
-
packageId: item.pkg.packageId,
|
|
317
|
-
capabilityId: item.capability.id,
|
|
318
|
-
connectionId: selectedConnections[item.key] || undefined,
|
|
319
|
-
});
|
|
320
|
-
} catch (error) {
|
|
321
|
-
web.value.settingsError =
|
|
322
|
-
error instanceof Error ? error.message : "Could not replace Assignment";
|
|
323
|
-
await web.value.loadBotSettings();
|
|
324
|
-
} finally {
|
|
325
|
-
assignmentBusy.value = undefined;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
async function unassignAssignment(
|
|
330
|
-
assignmentId: string,
|
|
331
|
-
key: string,
|
|
332
|
-
): Promise<void> {
|
|
333
|
-
assignmentBusy.value = key;
|
|
334
|
-
try {
|
|
335
|
-
await web.value.unassignCapability(assignmentId);
|
|
336
|
-
} catch (error) {
|
|
337
|
-
web.value.settingsError =
|
|
338
|
-
error instanceof Error ? error.message : "Could not unassign Capability";
|
|
339
|
-
await web.value.loadBotSettings();
|
|
340
|
-
} finally {
|
|
341
|
-
assignmentBusy.value = undefined;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async function unassign(
|
|
346
|
-
item: (typeof capabilityItems.value)[number],
|
|
347
|
-
): Promise<void> {
|
|
348
|
-
if (!item.existing) return;
|
|
349
|
-
await unassignAssignment(item.existing.assignmentId, item.key);
|
|
350
|
-
}
|
|
351
129
|
</script>
|
|
352
130
|
|
|
353
131
|
<template>
|
|
@@ -410,12 +188,6 @@ async function unassign(
|
|
|
410
188
|
</label>
|
|
411
189
|
</UiAnchor>
|
|
412
190
|
</div>
|
|
413
|
-
<!--
|
|
414
|
-
Pending decisions. The card in the conversation is where a decision is
|
|
415
|
-
normally answered; this is where the ones nobody scrolled back to are
|
|
416
|
-
still findable, because "a request for more authority becomes a durable
|
|
417
|
-
pending decision for the User" is only true if the User can find it.
|
|
418
|
-
-->
|
|
419
191
|
<UiAnchor
|
|
420
192
|
v-if="pendingApprovals.length > 0"
|
|
421
193
|
anchor="bot-approvals"
|
|
@@ -496,9 +268,7 @@ async function unassign(
|
|
|
496
268
|
<strong>Identity</strong>
|
|
497
269
|
<p>{{ name || "This Bot" }} · Bot {{ web.activeBotId }}</p>
|
|
498
270
|
</div>
|
|
499
|
-
<span>
|
|
500
|
-
Named by {{ web.botSettings?.profile.namedBy ?? "user" }}
|
|
501
|
-
</span>
|
|
271
|
+
<span>Named by {{ web.botSettings?.profile.namedBy ?? "user" }}</span>
|
|
502
272
|
</UiAnchor>
|
|
503
273
|
<UiAnchor
|
|
504
274
|
anchor="bot-info-members"
|
|
@@ -508,214 +278,9 @@ async function unassign(
|
|
|
508
278
|
>
|
|
509
279
|
<div>
|
|
510
280
|
<strong>Members</strong>
|
|
511
|
-
<p>This Bot
|
|
281
|
+
<p>This Bot uses what you enable for all of your Bots.</p>
|
|
512
282
|
</div>
|
|
513
|
-
<span>
|
|
514
|
-
{{ web.botSettings?.assignments.length ?? 0 }} Capability
|
|
515
|
-
Assignment(s)
|
|
516
|
-
</span>
|
|
517
|
-
</UiAnchor>
|
|
518
|
-
<p v-if="overriding" class="model-note">Overrides default model</p>
|
|
519
|
-
<UiAnchor
|
|
520
|
-
anchor="bot-model"
|
|
521
|
-
label="Model"
|
|
522
|
-
:href="link('bot-model')"
|
|
523
|
-
class="settings-row"
|
|
524
|
-
>
|
|
525
|
-
<fieldset class="model-mode">
|
|
526
|
-
<legend>Model</legend>
|
|
527
|
-
<label>
|
|
528
|
-
<input v-model="modelMode" type="radio" value="default" />
|
|
529
|
-
<span>Use default model ({{ defaultModelName }})</span>
|
|
530
|
-
</label>
|
|
531
|
-
<label>
|
|
532
|
-
<input v-model="modelMode" type="radio" value="custom" />
|
|
533
|
-
<span>Custom model</span>
|
|
534
|
-
</label>
|
|
535
|
-
</fieldset>
|
|
536
|
-
</UiAnchor>
|
|
537
|
-
<template v-if="modelMode === 'custom'">
|
|
538
|
-
<label class="exact-model-setting">
|
|
539
|
-
<span>
|
|
540
|
-
<strong>Use exact model ID</strong>
|
|
541
|
-
<small>Choose a model not listed in the advisory catalog.</small>
|
|
542
|
-
</span>
|
|
543
|
-
<input v-model="useExactModel" type="checkbox" />
|
|
544
|
-
</label>
|
|
545
|
-
<template v-if="useExactModel">
|
|
546
|
-
<UiField label="Connection">
|
|
547
|
-
<select v-model="exactConnectionId">
|
|
548
|
-
<option disabled value="">Select a Connection</option>
|
|
549
|
-
<option
|
|
550
|
-
v-for="connection in readyConnections"
|
|
551
|
-
:key="connection.connectionId"
|
|
552
|
-
:value="connection.connectionId"
|
|
553
|
-
>
|
|
554
|
-
{{ connection.displayName }}
|
|
555
|
-
</option>
|
|
556
|
-
</select>
|
|
557
|
-
</UiField>
|
|
558
|
-
<UiField label="Exact provider model ID">
|
|
559
|
-
<input
|
|
560
|
-
v-model="exactProviderModelId"
|
|
561
|
-
maxlength="256"
|
|
562
|
-
placeholder="model-name:cloud"
|
|
563
|
-
/>
|
|
564
|
-
</UiField>
|
|
565
|
-
</template>
|
|
566
|
-
<UiField v-else label="Model">
|
|
567
|
-
<select v-model="selectedModel">
|
|
568
|
-
<option disabled value="">Select a connected model</option>
|
|
569
|
-
<option
|
|
570
|
-
v-for="model in modelOptions"
|
|
571
|
-
:key="model.value"
|
|
572
|
-
:value="model.value"
|
|
573
|
-
>
|
|
574
|
-
{{ model.label }}
|
|
575
|
-
</option>
|
|
576
|
-
</select>
|
|
577
|
-
</UiField>
|
|
578
|
-
</template>
|
|
579
|
-
<UiAnchor
|
|
580
|
-
anchor="bot-capabilities"
|
|
581
|
-
label="Capability Assignments"
|
|
582
|
-
:href="link('bot-capabilities')"
|
|
583
|
-
class="settings-row"
|
|
584
|
-
>
|
|
585
|
-
<section class="assignment-settings">
|
|
586
|
-
<div>
|
|
587
|
-
<strong>Capability Assignments</strong>
|
|
588
|
-
<p>
|
|
589
|
-
Grant this Bot an installed Capability and required Connection.
|
|
590
|
-
</p>
|
|
591
|
-
</div>
|
|
592
|
-
<article
|
|
593
|
-
v-for="item in capabilityItems"
|
|
594
|
-
:key="item.key"
|
|
595
|
-
class="assignment-card"
|
|
596
|
-
>
|
|
597
|
-
<div>
|
|
598
|
-
<strong
|
|
599
|
-
>{{ item.pkg.displayName }} · {{ item.capability.id }}</strong
|
|
600
|
-
>
|
|
601
|
-
<small v-if="item.pending">
|
|
602
|
-
{{ item.pending.kind }} · {{ item.pending.state }}
|
|
603
|
-
</small>
|
|
604
|
-
<small v-else-if="item.existing">
|
|
605
|
-
{{ item.existing.state }}
|
|
606
|
-
</small>
|
|
607
|
-
<small v-else>Not assigned</small>
|
|
608
|
-
</div>
|
|
609
|
-
<select
|
|
610
|
-
v-if="item.capability.connectionTypes.length > 0"
|
|
611
|
-
v-model="selectedConnections[item.key]"
|
|
612
|
-
:disabled="Boolean(item.pending)"
|
|
613
|
-
:aria-label="`Connection for ${item.capability.id}`"
|
|
614
|
-
>
|
|
615
|
-
<option value="">Choose a ready Connection</option>
|
|
616
|
-
<option
|
|
617
|
-
v-for="connection in item.connections"
|
|
618
|
-
:key="connection.connectionId"
|
|
619
|
-
:value="connection.connectionId"
|
|
620
|
-
>
|
|
621
|
-
{{ connection.displayName }}
|
|
622
|
-
</option>
|
|
623
|
-
</select>
|
|
624
|
-
<div class="assignment-actions">
|
|
625
|
-
<UiButton
|
|
626
|
-
v-if="!item.existing"
|
|
627
|
-
type="button"
|
|
628
|
-
:disabled="
|
|
629
|
-
Boolean(item.pending) ||
|
|
630
|
-
assignmentBusy === item.key ||
|
|
631
|
-
(item.capability.connectionTypes.length > 0 &&
|
|
632
|
-
!selectedConnections[item.key])
|
|
633
|
-
"
|
|
634
|
-
@click="assign(item)"
|
|
635
|
-
>
|
|
636
|
-
Assign
|
|
637
|
-
</UiButton>
|
|
638
|
-
<template v-else>
|
|
639
|
-
<UiButton
|
|
640
|
-
type="button"
|
|
641
|
-
:disabled="
|
|
642
|
-
Boolean(item.pending) || assignmentBusy === item.key
|
|
643
|
-
"
|
|
644
|
-
@click="replace(item)"
|
|
645
|
-
>
|
|
646
|
-
Replace
|
|
647
|
-
</UiButton>
|
|
648
|
-
<UiButton
|
|
649
|
-
type="button"
|
|
650
|
-
variant="danger"
|
|
651
|
-
:disabled="
|
|
652
|
-
Boolean(item.pending) || assignmentBusy === item.key
|
|
653
|
-
"
|
|
654
|
-
@click="unassign(item)"
|
|
655
|
-
>
|
|
656
|
-
Unassign
|
|
657
|
-
</UiButton>
|
|
658
|
-
</template>
|
|
659
|
-
</div>
|
|
660
|
-
</article>
|
|
661
|
-
<article
|
|
662
|
-
v-for="operation in assignmentOperations"
|
|
663
|
-
:key="`operation:${operation.commandId}`"
|
|
664
|
-
class="assignment-card"
|
|
665
|
-
data-assignment-operation
|
|
666
|
-
>
|
|
667
|
-
<div>
|
|
668
|
-
<strong>
|
|
669
|
-
{{ operation.target?.packageId ?? "Unavailable Package" }} ·
|
|
670
|
-
{{ operation.target?.capabilityId ?? operation.assignmentId }}
|
|
671
|
-
</strong>
|
|
672
|
-
<small>{{ operation.kind }} · {{ operation.state }}</small>
|
|
673
|
-
</div>
|
|
674
|
-
</article>
|
|
675
|
-
<article
|
|
676
|
-
v-for="assignment in orphanAssignments"
|
|
677
|
-
:key="assignment.assignmentId"
|
|
678
|
-
class="assignment-card"
|
|
679
|
-
>
|
|
680
|
-
<div>
|
|
681
|
-
<strong
|
|
682
|
-
>{{ assignment.packageId }} ·
|
|
683
|
-
{{ assignment.capabilityId }}</strong
|
|
684
|
-
>
|
|
685
|
-
<small
|
|
686
|
-
>{{ assignment.state }} · no longer available in the
|
|
687
|
-
catalog</small
|
|
688
|
-
>
|
|
689
|
-
</div>
|
|
690
|
-
<div class="assignment-actions">
|
|
691
|
-
<UiButton
|
|
692
|
-
type="button"
|
|
693
|
-
variant="danger"
|
|
694
|
-
:disabled="
|
|
695
|
-
assignmentBusy === assignment.assignmentId ||
|
|
696
|
-
assignmentOperationPending(assignment.assignmentId)
|
|
697
|
-
"
|
|
698
|
-
@click="
|
|
699
|
-
unassignAssignment(
|
|
700
|
-
assignment.assignmentId,
|
|
701
|
-
assignment.assignmentId,
|
|
702
|
-
)
|
|
703
|
-
"
|
|
704
|
-
>
|
|
705
|
-
Unassign
|
|
706
|
-
</UiButton>
|
|
707
|
-
</div>
|
|
708
|
-
</article>
|
|
709
|
-
<p
|
|
710
|
-
v-if="
|
|
711
|
-
capabilityItems.length === 0 && orphanAssignments.length === 0
|
|
712
|
-
"
|
|
713
|
-
class="assignment-empty"
|
|
714
|
-
>
|
|
715
|
-
No assignable Capabilities are available in the production
|
|
716
|
-
catalog.
|
|
717
|
-
</p>
|
|
718
|
-
</section>
|
|
283
|
+
<span>Packages and Connections are shared</span>
|
|
719
284
|
</UiAnchor>
|
|
720
285
|
<k-slot name="frockbot.bot-settings-sections" />
|
|
721
286
|
</div>
|
|
@@ -735,10 +300,6 @@ async function unassign(
|
|
|
735
300
|
</template>
|
|
736
301
|
|
|
737
302
|
<style scoped>
|
|
738
|
-
/*
|
|
739
|
-
* A deep-linkable row. The anchor floats its copy control in the top-right
|
|
740
|
-
* corner, so every row keeps that corner clear.
|
|
741
|
-
*/
|
|
742
303
|
.settings-row {
|
|
743
304
|
display: flex;
|
|
744
305
|
flex-direction: column;
|
|
@@ -753,7 +314,6 @@ async function unassign(
|
|
|
753
314
|
padding: 16px;
|
|
754
315
|
}
|
|
755
316
|
|
|
756
|
-
.assignment-card,
|
|
757
317
|
.notification-setting,
|
|
758
318
|
.bot-members {
|
|
759
319
|
border: 1px solid var(--frock-border);
|
|
@@ -769,19 +329,6 @@ async function unassign(
|
|
|
769
329
|
padding-right: 0;
|
|
770
330
|
}
|
|
771
331
|
|
|
772
|
-
.assignment-settings p,
|
|
773
|
-
.assignment-card small {
|
|
774
|
-
margin-top: 4px;
|
|
775
|
-
color: var(--frock-text-muted);
|
|
776
|
-
font-size: var(--frock-text-sm);
|
|
777
|
-
line-height: var(--frock-leading-normal);
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
.assignment-settings {
|
|
781
|
-
display: grid;
|
|
782
|
-
gap: 10px;
|
|
783
|
-
}
|
|
784
|
-
|
|
785
332
|
.bot-members {
|
|
786
333
|
display: flex;
|
|
787
334
|
align-items: center;
|
|
@@ -809,91 +356,35 @@ async function unassign(
|
|
|
809
356
|
}
|
|
810
357
|
|
|
811
358
|
.bot-members > span {
|
|
812
|
-
max-width:
|
|
359
|
+
max-width: 120px;
|
|
813
360
|
flex: 0 0 auto;
|
|
814
361
|
text-align: right;
|
|
815
362
|
}
|
|
816
363
|
|
|
817
|
-
.assignment-settings strong {
|
|
818
|
-
font-size: var(--frock-text-md);
|
|
819
|
-
font-weight: 600;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
.assignment-card {
|
|
823
|
-
display: grid;
|
|
824
|
-
gap: 8px;
|
|
825
|
-
padding: 12px;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
.assignment-card strong {
|
|
829
|
-
font-size: var(--frock-text-md);
|
|
830
|
-
font-weight: 600;
|
|
831
|
-
overflow-wrap: anywhere;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
.assignment-card small {
|
|
835
|
-
display: block;
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
.assignment-card select {
|
|
839
|
-
width: 100%;
|
|
840
|
-
}
|
|
841
|
-
|
|
842
|
-
.assignment-actions {
|
|
843
|
-
display: flex;
|
|
844
|
-
flex-wrap: wrap;
|
|
845
|
-
justify-content: flex-end;
|
|
846
|
-
gap: 8px;
|
|
847
|
-
}
|
|
848
|
-
|
|
849
|
-
/* The right panel is ~360px wide, so Assignment controls stay compact. */
|
|
850
|
-
.assignment-actions :deep(.ui-button) {
|
|
851
|
-
min-height: 30px;
|
|
852
|
-
padding: 0 10px;
|
|
853
|
-
font-size: var(--frock-text-sm);
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
.assignment-empty {
|
|
857
|
-
padding: 12px;
|
|
858
|
-
color: var(--frock-text-muted);
|
|
859
|
-
font-size: var(--frock-text-sm);
|
|
860
|
-
border: 1px dashed var(--frock-border);
|
|
861
|
-
border-radius: var(--frock-radius-card);
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
.exact-model-setting,
|
|
865
364
|
.notification-setting {
|
|
866
365
|
display: flex;
|
|
867
366
|
align-items: center;
|
|
868
367
|
justify-content: space-between;
|
|
869
368
|
gap: 12px;
|
|
870
369
|
padding: 12px;
|
|
871
|
-
border: 1px solid var(--frock-border);
|
|
872
|
-
border-radius: var(--frock-radius-card);
|
|
873
|
-
background: var(--frock-surface-subtle);
|
|
874
370
|
}
|
|
875
371
|
|
|
876
|
-
.exact-model-setting strong,
|
|
877
|
-
.exact-model-setting small,
|
|
878
372
|
.notification-setting strong,
|
|
879
373
|
.notification-setting small {
|
|
880
374
|
display: block;
|
|
881
375
|
}
|
|
882
376
|
|
|
883
|
-
.notification-setting strong
|
|
884
|
-
.exact-model-setting strong {
|
|
377
|
+
.notification-setting strong {
|
|
885
378
|
font-size: var(--frock-text-md);
|
|
886
379
|
font-weight: 600;
|
|
887
380
|
}
|
|
888
381
|
|
|
889
|
-
.exact-model-setting small,
|
|
890
382
|
.notification-setting small {
|
|
891
383
|
margin-top: 4px;
|
|
892
384
|
color: var(--frock-text-muted);
|
|
893
385
|
font-size: var(--frock-text-sm);
|
|
894
386
|
}
|
|
895
387
|
|
|
896
|
-
.exact-model-setting input[type="checkbox"],
|
|
897
388
|
.notification-setting input[type="checkbox"] {
|
|
898
389
|
position: relative;
|
|
899
390
|
width: 38px;
|
|
@@ -907,7 +398,6 @@ async function unassign(
|
|
|
907
398
|
transition: background-color var(--frock-motion-fast);
|
|
908
399
|
}
|
|
909
400
|
|
|
910
|
-
.exact-model-setting input[type="checkbox"]::before,
|
|
911
401
|
.notification-setting input[type="checkbox"]::before {
|
|
912
402
|
position: absolute;
|
|
913
403
|
top: 2px;
|
|
@@ -921,18 +411,15 @@ async function unassign(
|
|
|
921
411
|
transition: transform var(--frock-motion-fast);
|
|
922
412
|
}
|
|
923
413
|
|
|
924
|
-
.exact-model-setting input[type="checkbox"]:checked,
|
|
925
414
|
.notification-setting input[type="checkbox"]:checked {
|
|
926
415
|
border-color: var(--frock-action-primary);
|
|
927
416
|
background: var(--frock-action-primary);
|
|
928
417
|
}
|
|
929
418
|
|
|
930
|
-
.exact-model-setting input[type="checkbox"]:checked::before,
|
|
931
419
|
.notification-setting input[type="checkbox"]:checked::before {
|
|
932
420
|
transform: translateX(16px);
|
|
933
421
|
}
|
|
934
422
|
|
|
935
|
-
.exact-model-setting input[type="checkbox"]:focus-visible,
|
|
936
423
|
.notification-setting input[type="checkbox"]:focus-visible {
|
|
937
424
|
outline: 2px solid var(--frock-focus-ring);
|
|
938
425
|
outline-offset: 2px;
|
|
@@ -978,12 +465,24 @@ async function unassign(
|
|
|
978
465
|
gap: 0.5rem;
|
|
979
466
|
}
|
|
980
467
|
|
|
981
|
-
.
|
|
468
|
+
.primary-contributions {
|
|
469
|
+
display: flex;
|
|
470
|
+
flex-direction: column;
|
|
471
|
+
gap: 8px;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
.settings-error {
|
|
982
475
|
margin: 0;
|
|
983
|
-
color: var(--frock-text
|
|
476
|
+
color: var(--frock-danger-text);
|
|
984
477
|
font-size: var(--frock-text-sm);
|
|
985
478
|
}
|
|
986
479
|
|
|
480
|
+
.settings-actions {
|
|
481
|
+
display: flex;
|
|
482
|
+
justify-content: flex-end;
|
|
483
|
+
gap: 8px;
|
|
484
|
+
}
|
|
485
|
+
|
|
987
486
|
.advanced {
|
|
988
487
|
border-top: 1px solid var(--frock-border);
|
|
989
488
|
padding-top: 12px;
|
|
@@ -1018,59 +517,7 @@ async function unassign(
|
|
|
1018
517
|
.advanced__body {
|
|
1019
518
|
display: flex;
|
|
1020
519
|
flex-direction: column;
|
|
1021
|
-
gap:
|
|
520
|
+
gap: 16px;
|
|
1022
521
|
padding-top: 12px;
|
|
1023
522
|
}
|
|
1024
|
-
|
|
1025
|
-
.model-mode {
|
|
1026
|
-
display: flex;
|
|
1027
|
-
flex-direction: column;
|
|
1028
|
-
gap: 10px;
|
|
1029
|
-
margin: 0;
|
|
1030
|
-
border: 0;
|
|
1031
|
-
padding: 0;
|
|
1032
|
-
}
|
|
1033
|
-
|
|
1034
|
-
.model-mode legend {
|
|
1035
|
-
float: left;
|
|
1036
|
-
width: 100%;
|
|
1037
|
-
margin-bottom: 4px;
|
|
1038
|
-
padding: 0;
|
|
1039
|
-
color: var(--frock-text);
|
|
1040
|
-
font-size: var(--frock-text-md);
|
|
1041
|
-
font-weight: 600;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
.model-mode label {
|
|
1045
|
-
display: flex;
|
|
1046
|
-
align-items: center;
|
|
1047
|
-
gap: 8px;
|
|
1048
|
-
color: var(--frock-text);
|
|
1049
|
-
font-size: var(--frock-text-md);
|
|
1050
|
-
cursor: pointer;
|
|
1051
|
-
}
|
|
1052
|
-
|
|
1053
|
-
.model-mode input {
|
|
1054
|
-
width: 17px;
|
|
1055
|
-
height: 17px;
|
|
1056
|
-
flex: 0 0 auto;
|
|
1057
|
-
accent-color: var(--frock-action-primary);
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
.settings-error {
|
|
1061
|
-
margin: 0;
|
|
1062
|
-
color: var(--frock-danger-text);
|
|
1063
|
-
font-size: var(--frock-text-sm);
|
|
1064
|
-
}
|
|
1065
|
-
|
|
1066
|
-
.settings-actions {
|
|
1067
|
-
display: flex;
|
|
1068
|
-
justify-content: flex-end;
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
.primary-contributions {
|
|
1072
|
-
display: flex;
|
|
1073
|
-
flex-direction: column;
|
|
1074
|
-
gap: 12px;
|
|
1075
|
-
}
|
|
1076
523
|
</style>
|