slotlock 0.7.0 → 0.8.0
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/CHANGELOG.md +44 -0
- package/assistant.d.ts +30 -1
- package/assistant.js +369 -56
- package/dashboard-assistant-ui.d.ts +21 -0
- package/dashboard-assistant-ui.js +438 -0
- package/dashboard-markdown.d.ts +2 -0
- package/dashboard-markdown.js +157 -0
- package/dashboard-settings-ui.d.ts +1 -0
- package/dashboard-settings-ui.js +78 -0
- package/dashboard.d.ts +29 -5
- package/dashboard.js +900 -315
- package/package.json +1 -1
- package/slotlock.manifest.json +1 -1
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// The dashboard Settings pages' styles: a section list beside one column of cards, label-left rows,
|
|
2
|
+
// provider tiles, and the assistant's status card. dashboard.ts renders the pages.
|
|
3
|
+
export const SLOTLOCK_SETTINGS_CSS = `
|
|
4
|
+
.settings { display: grid; grid-template-columns: 176px minmax(0, 640px); gap: var(--space-8); align-items: start; }
|
|
5
|
+
.subnav { display: grid; gap: 2px; position: sticky; top: var(--space-6); }
|
|
6
|
+
.subnav a { display: flex; align-items: center; gap: var(--space-2); height: 32px; padding: 0 var(--space-3); border-radius: var(--radius-sm); color: var(--ink-2); font-size: 14px; text-decoration: none; }
|
|
7
|
+
.subnav a:hover { background: var(--hover); color: var(--ink); }
|
|
8
|
+
.subnav a[aria-current="page"] { background: var(--surface); color: var(--ink); font-weight: 500; box-shadow: inset 0 0 0 1px var(--line); }
|
|
9
|
+
.subnav .nav-dot { width: 6px; height: 6px; border-radius: 50%; margin-left: auto; background: var(--good); }
|
|
10
|
+
.settings-body { display: grid; gap: var(--space-6); min-width: 0; }
|
|
11
|
+
.settings-body > .notice { margin: 0; }
|
|
12
|
+
.card-head { margin: 0 0 calc(var(--space-2) - var(--space-6)); font-size: 13px; font-weight: 600; color: var(--ink-3); }
|
|
13
|
+
.set-card { background: var(--surface); border: 1px solid var(--line); border-radius: 10px; overflow: hidden; margin: 0; }
|
|
14
|
+
.row { display: grid; grid-template-columns: minmax(120px, 160px) minmax(0, 1fr); gap: var(--space-4); align-items: center; min-height: 56px; padding: 12px 20px; border-top: 1px solid var(--line-soft); }
|
|
15
|
+
.row.stack { grid-template-columns: minmax(0, 1fr); gap: var(--space-3); align-items: start; padding-top: 16px; padding-bottom: 16px; }
|
|
16
|
+
.row:first-child { border-top: 0; }
|
|
17
|
+
.row-label { font-size: 14px; font-weight: 500; color: var(--ink); }
|
|
18
|
+
.row-value { font-size: 14px; color: var(--ink); min-width: 0; overflow-wrap: anywhere; }
|
|
19
|
+
.row-value.muted { color: var(--ink-2); }
|
|
20
|
+
.row input:not([type="radio"]):not([type="checkbox"]), .row select { width: 100%; }
|
|
21
|
+
.mono { font-family: var(--mono); font-size: 13px; }
|
|
22
|
+
.card-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: 12px 20px; border-top: 1px solid var(--line); background: color-mix(in srgb, var(--surface) 70%, var(--canvas)); }
|
|
23
|
+
.card-foot .fine { margin: 0; }
|
|
24
|
+
.card-foot .primary { flex: none; }
|
|
25
|
+
.status-head { display: grid; grid-template-columns: 32px minmax(0, 1fr) auto auto; gap: var(--space-3); align-items: center; padding: 16px 20px; }
|
|
26
|
+
.status-head form { margin: 0; }
|
|
27
|
+
.mono-tile { display: grid; place-items: center; width: 32px; height: 32px; border-radius: var(--radius); background: var(--sunken); border: 1px solid var(--line); color: var(--ink); font-weight: 600; font-size: 14px; }
|
|
28
|
+
.status-name { display: grid; gap: 2px; min-width: 0; }
|
|
29
|
+
.status-name strong { font-size: 15px; font-weight: 600; color: var(--ink); }
|
|
30
|
+
.status-name code { color: var(--ink-3); font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; background: none; padding: 0; }
|
|
31
|
+
.badge { display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 8px; border-radius: var(--radius-full); font-size: 12px; font-weight: 500; color: var(--ink-2); background: var(--sunken); white-space: nowrap; }
|
|
32
|
+
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--ink-3); }
|
|
33
|
+
.badge.ok { color: var(--good); background: color-mix(in srgb, var(--good) 14%, transparent); }
|
|
34
|
+
.badge.ok::before { background: var(--good); }
|
|
35
|
+
.badge.bad { color: var(--bad); background: color-mix(in srgb, var(--bad) 14%, transparent); }
|
|
36
|
+
.badge.bad::before { background: var(--bad); }
|
|
37
|
+
.status-card .notice { margin: 0; border-radius: 0; border-width: 1px 0 0; box-shadow: none; font-size: 13px; padding: 10px 20px; }
|
|
38
|
+
.status-card .notice.done { color: var(--ink); }
|
|
39
|
+
.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--space-2); }
|
|
40
|
+
.tile { position: relative; display: flex; align-items: center; gap: var(--space-2); height: 40px; padding: 0 10px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); font-size: 14px; color: var(--ink); cursor: pointer; transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease); }
|
|
41
|
+
.tile:hover { background: var(--hover); }
|
|
42
|
+
.tile input { position: absolute; opacity: 0; pointer-events: none; }
|
|
43
|
+
.tile .tile-mark { display: grid; place-items: center; flex: none; width: 20px; height: 20px; border-radius: 5px; background: var(--sunken); color: var(--ink-2); font-size: 11px; font-weight: 600; }
|
|
44
|
+
.tile:has(input:checked) { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
|
|
45
|
+
.tile:has(input:checked) .tile-mark { background: var(--accent); color: var(--surface); }
|
|
46
|
+
.tile:has(input:focus-visible) { box-shadow: 0 0 0 3px var(--ring); }
|
|
47
|
+
.provider-form:not(:has(input[name="preset"][value="custom"]:checked)) .custom-only { display: none; }
|
|
48
|
+
.segmented { justify-self: start; display: inline-flex; padding: 2px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--sunken); gap: 2px; }
|
|
49
|
+
.segmented label { position: relative; display: inline-flex; align-items: center; height: 30px; padding: 0 12px; border-radius: 6px; font-size: 13px; color: var(--ink-2); cursor: pointer; }
|
|
50
|
+
.segmented input { position: absolute; opacity: 0; pointer-events: none; }
|
|
51
|
+
.segmented label:has(input:checked) { background: var(--surface); color: var(--ink); box-shadow: 0 0 0 1px var(--line); }
|
|
52
|
+
.field-with-button { display: flex; gap: var(--space-2); align-items: center; }
|
|
53
|
+
.field-with-button input { flex: 1; min-width: 0; }
|
|
54
|
+
.field-with-button button { flex: none; }
|
|
55
|
+
.key-set { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
|
|
56
|
+
.key-set code { background: none; padding: 0; color: var(--ink-2); letter-spacing: 0.04em; }
|
|
57
|
+
.key-set details { display: contents; }
|
|
58
|
+
.key-set details > summary { list-style: none; cursor: pointer; color: var(--accent); font-size: 13px; }
|
|
59
|
+
.key-set details > summary::-webkit-details-marker { display: none; }
|
|
60
|
+
.key-set details[open] > summary { display: none; }
|
|
61
|
+
.key-set details[open] input { flex-basis: 100%; }
|
|
62
|
+
.danger-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); padding: 14px 20px; }
|
|
63
|
+
.danger-row strong { font-size: 14px; font-weight: 500; color: var(--ink); }
|
|
64
|
+
.danger-row details.act > summary { color: var(--bad); }
|
|
65
|
+
@media (max-width: 760px) {
|
|
66
|
+
.settings { grid-template-columns: minmax(0, 1fr); gap: var(--space-4); }
|
|
67
|
+
.subnav { position: static; display: flex; overflow-x: auto; gap: 0; border-bottom: 1px solid var(--line); scrollbar-width: none; }
|
|
68
|
+
.subnav::-webkit-scrollbar { display: none; }
|
|
69
|
+
.subnav a { height: 40px; border-radius: 0; flex: none; }
|
|
70
|
+
.subnav a[aria-current="page"] { background: none; box-shadow: inset 0 -2px 0 var(--accent); }
|
|
71
|
+
.row { grid-template-columns: minmax(0, 1fr); gap: var(--space-2); padding: 12px 16px; }
|
|
72
|
+
.tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
|
73
|
+
.status-head { grid-template-columns: 32px minmax(0, 1fr) auto; padding: 14px 16px; }
|
|
74
|
+
.status-head form { grid-column: 1 / -1; }
|
|
75
|
+
.card-foot { flex-direction: column; align-items: stretch; padding: 12px 16px; }
|
|
76
|
+
.card-foot .primary { height: 44px; }
|
|
77
|
+
}
|
|
78
|
+
`;
|
package/dashboard.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { type SlotlockAgentCalendarBackend, type SlotlockAgentOperation, type Sl
|
|
|
4
4
|
import { type SlotlockAssistantStore } from './assistant.js';
|
|
5
5
|
import { type SlotlockRate } from './config.js';
|
|
6
6
|
import { type SlotlockBookingRules } from './booking-rules.js';
|
|
7
|
-
import { type SlotlockTenantPlan } from './plans.js';
|
|
7
|
+
import { type SlotlockPlan, type SlotlockTenantPlan } from './plans.js';
|
|
8
8
|
import { type SlotlockLocation, type SlotlockResourceGroup, type SlotlockResourceProfileInput } from './resource-profile.js';
|
|
9
9
|
import { type SlotlockUsageRow } from './usage.js';
|
|
10
10
|
import { type CreateWebhookEndpointResult, type SlotlockChange, type SlotlockWebhookEndpoint } from './event-feed.js';
|
|
@@ -233,9 +233,31 @@ export interface SlotlockDashboardAssistantOptions {
|
|
|
233
233
|
conversationTtlMs?: number;
|
|
234
234
|
}
|
|
235
235
|
/**
|
|
236
|
-
* Hosted Slotlock's plans (plans.ts) and their billing: a
|
|
236
|
+
* Hosted Slotlock's plans (plans.ts) and their billing: a Usage page with the tenant's plan and use,
|
|
237
237
|
* Upgrade (to a checkout page) and Manage (to a billing portal). Off unless set.
|
|
238
238
|
*/
|
|
239
|
+
/**
|
|
240
|
+
* The tenant's plan and the deployment's default, read whether or not anything sells plans: with it
|
|
241
|
+
* the dashboard shows a Usage page (plan, limits and this month's use) and puts a tenant new to
|
|
242
|
+
* plans on the default before it adds what plans limit.
|
|
243
|
+
*/
|
|
244
|
+
export interface SlotlockDashboardPlans {
|
|
245
|
+
/** The tenant's plan (its own, or the default once it applies), or null (the server's limits apply). */
|
|
246
|
+
plan(tenantRef: string): Promise<SlotlockTenantPlan | null>;
|
|
247
|
+
/** Puts a tenant new to plans on the default plan before it adds a key, resource or webhook endpoint. */
|
|
248
|
+
enrol?(tenantRef: string): Promise<void>;
|
|
249
|
+
/** The deployment's default plan and the date it applies to every tenant, or null. */
|
|
250
|
+
planDefault?(): Promise<{
|
|
251
|
+
plan: SlotlockPlan;
|
|
252
|
+
limits: SlotlockTenantPlan['limits'];
|
|
253
|
+
appliesFrom: Date;
|
|
254
|
+
} | null>;
|
|
255
|
+
/**
|
|
256
|
+
* Where asking for another plan goes (an https or mailto URL) when nothing sells plans here: with
|
|
257
|
+
* it, the Usage page offers Developer or Team by asking.
|
|
258
|
+
*/
|
|
259
|
+
contact?: string;
|
|
260
|
+
}
|
|
239
261
|
export interface SlotlockDashboardBilling {
|
|
240
262
|
/** The tenant's plan (its own, or the default), or null when it has none (the server's limits apply). */
|
|
241
263
|
plan(tenantRef: string): Promise<SlotlockTenantPlan | null>;
|
|
@@ -359,15 +381,17 @@ export interface SlotlockDashboardOptions {
|
|
|
359
381
|
availability?: readonly WeeklyAvailabilityRule[];
|
|
360
382
|
/** The chat assistant; off unless set. */
|
|
361
383
|
assistant?: SlotlockDashboardAssistantOptions;
|
|
362
|
-
/** Plans
|
|
384
|
+
/** Plans (hosted Slotlock): a Usage page, and enrolment on the default plan. Off unless set. */
|
|
385
|
+
plans?: SlotlockDashboardPlans;
|
|
386
|
+
/** Paying for plans through Stripe (hosted Slotlock); off unless set. Implies `plans` from its own. */
|
|
363
387
|
billing?: SlotlockDashboardBilling;
|
|
364
388
|
/**
|
|
365
|
-
* The deployment meters usage (SLOTLOCK_USAGE=on): the
|
|
389
|
+
* The deployment meters usage (SLOTLOCK_USAGE=on): the Usage page shows this month's, from the
|
|
366
390
|
* resources adapter's `usage`.
|
|
367
391
|
*/
|
|
368
392
|
usage?: boolean;
|
|
369
393
|
/**
|
|
370
|
-
* The deployment holds tenants to their plans' monthly quotas (SLOTLOCK_QUOTAS=on): the
|
|
394
|
+
* The deployment holds tenants to their plans' monthly quotas (SLOTLOCK_QUOTAS=on): the Usage page
|
|
371
395
|
* shows this month's use against them, and warns from 80%. Needs `usage`.
|
|
372
396
|
*/
|
|
373
397
|
quotas?: boolean;
|