@xenosystem/blocks 0.10.0 → 0.11.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/dist/auth/index.d.ts +105 -5
- package/dist/auth/index.js +408 -215
- package/package.json +3 -3
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
import { PanelModule, PanelManifest, BlockDeclaration } from '@xenosystem/block-sdk';
|
|
4
|
+
import { PanelTone } from '@xenosystem/components/chrome';
|
|
4
5
|
|
|
5
6
|
interface CreateAuthGatePanelOptions {
|
|
6
7
|
/** What renders behind the gate once it is `open`. A product passes its app; the harness a fixture. */
|
|
@@ -371,9 +372,6 @@ declare function plateCopy(phase: XenoAuthGatePhase, product: string, message: s
|
|
|
371
372
|
body: string;
|
|
372
373
|
note?: string;
|
|
373
374
|
};
|
|
374
|
-
declare const BEHAVIOURS: {
|
|
375
|
-
'auth/authGateView': (inputs: Record<string, unknown>, config: Readonly<Record<string, unknown>>) => AuthGateView;
|
|
376
|
-
};
|
|
377
375
|
|
|
378
376
|
/**
|
|
379
377
|
* `mountXenoAuthGate` — the per-product plug, exactly as `XENO AUTH - AUTH GATE DELTA.md` §4 specifies:
|
|
@@ -611,7 +609,109 @@ interface AccountPanelViewProps {
|
|
|
611
609
|
showEntitlements?: boolean;
|
|
612
610
|
emptyHint?: string;
|
|
613
611
|
}
|
|
614
|
-
/**
|
|
612
|
+
/**
|
|
613
|
+
* The Account panel view — the code twin of `ACCOUNT_DECLARATION`, rendering the SAME model
|
|
614
|
+
* (`accountPanelView`) so the two cannot drift; `test/auth-account/declaration.test.tsx` holds the markup
|
|
615
|
+
* byte-identical in every status.
|
|
616
|
+
*/
|
|
615
617
|
declare function AccountPanelView({ controller, showLedger, showEntitlements, emptyHint, }: AccountPanelViewProps): ReactNode;
|
|
616
618
|
|
|
617
|
-
|
|
619
|
+
/**
|
|
620
|
+
* The account panel AS A DECLARATION — the third block written the §8d way (auth gate, agent, account).
|
|
621
|
+
* Chrome units + bindings + emits over ONE behaviour, `auth/accountPanelView`, which derives every label,
|
|
622
|
+
* tone and row shape from `AccountViewState`. The code view renders the same model; the test holds the
|
|
623
|
+
* two byte-identical in every account status.
|
|
624
|
+
*
|
|
625
|
+
* Presentation contract: input `view` (an `AccountViewState` push from the controller), output `ui`
|
|
626
|
+
* (`{ kind, seatId?, expanded? }`), applied by the controller (`request(kind)`, `refresh()`,
|
|
627
|
+
* `setLedgerExpanded()`). The declaration holds no state.
|
|
628
|
+
*/
|
|
629
|
+
|
|
630
|
+
type AccountUiIntent = {
|
|
631
|
+
kind: 'signIn';
|
|
632
|
+
} | {
|
|
633
|
+
kind: 'signOut';
|
|
634
|
+
} | {
|
|
635
|
+
kind: 'manageBilling';
|
|
636
|
+
} | {
|
|
637
|
+
kind: 'refresh';
|
|
638
|
+
} | {
|
|
639
|
+
kind: 'switchSeat';
|
|
640
|
+
seatId: string;
|
|
641
|
+
} | {
|
|
642
|
+
kind: 'ledgerCollapsed';
|
|
643
|
+
collapsed: boolean;
|
|
644
|
+
};
|
|
645
|
+
declare const accountViewManifest: PanelManifest;
|
|
646
|
+
declare const STATUS_LABEL: Record<XenoAccountStatus, string>;
|
|
647
|
+
declare const STATUS_TONE: Record<XenoAccountStatus, PanelTone>;
|
|
648
|
+
interface AccountPanelViewModel {
|
|
649
|
+
loading: boolean;
|
|
650
|
+
signedOut: boolean;
|
|
651
|
+
signedIn: boolean;
|
|
652
|
+
loadingHint: string;
|
|
653
|
+
statusLabel: string;
|
|
654
|
+
statusTone: PanelTone;
|
|
655
|
+
signedOutHint: string;
|
|
656
|
+
planLabel?: string;
|
|
657
|
+
planTitle?: string;
|
|
658
|
+
name?: string;
|
|
659
|
+
email?: string;
|
|
660
|
+
message?: string;
|
|
661
|
+
hasCredits: boolean;
|
|
662
|
+
creditsUnit: string;
|
|
663
|
+
creditsBalance: string;
|
|
664
|
+
creditsSub?: string;
|
|
665
|
+
creditsStale: boolean;
|
|
666
|
+
creditsTitle: string;
|
|
667
|
+
hasIncluded: boolean;
|
|
668
|
+
includedValue: string;
|
|
669
|
+
includedSub?: string;
|
|
670
|
+
showSeats: boolean;
|
|
671
|
+
seats: {
|
|
672
|
+
id: string;
|
|
673
|
+
label: string;
|
|
674
|
+
active: boolean;
|
|
675
|
+
inactive: boolean;
|
|
676
|
+
}[];
|
|
677
|
+
showEntitlements: boolean;
|
|
678
|
+
entitlements: {
|
|
679
|
+
id: string;
|
|
680
|
+
label: string;
|
|
681
|
+
detail?: string;
|
|
682
|
+
tone: PanelTone;
|
|
683
|
+
text: string;
|
|
684
|
+
}[];
|
|
685
|
+
showLedger: boolean;
|
|
686
|
+
ledgerCollapsed: boolean;
|
|
687
|
+
ledgerCount?: string;
|
|
688
|
+
hasLedger: boolean;
|
|
689
|
+
ledgerEmpty: boolean;
|
|
690
|
+
ledger: {
|
|
691
|
+
id: string;
|
|
692
|
+
description: string;
|
|
693
|
+
source?: string;
|
|
694
|
+
amount: string;
|
|
695
|
+
}[];
|
|
696
|
+
ledgerHasMore: boolean;
|
|
697
|
+
renews?: string;
|
|
698
|
+
offlineNote?: string;
|
|
699
|
+
}
|
|
700
|
+
interface AccountPanelViewOptions {
|
|
701
|
+
showLedger?: boolean;
|
|
702
|
+
showEntitlements?: boolean;
|
|
703
|
+
emptyHint?: string;
|
|
704
|
+
}
|
|
705
|
+
declare function accountPanelView(state: AccountViewState, options?: AccountPanelViewOptions): AccountPanelViewModel;
|
|
706
|
+
declare const BEHAVIOURS$1: {
|
|
707
|
+
'auth/accountPanelView': (inputs: Record<string, unknown>, config: Readonly<Record<string, unknown>>) => AccountPanelViewModel;
|
|
708
|
+
};
|
|
709
|
+
declare const ACCOUNT_DECLARATION: BlockDeclaration;
|
|
710
|
+
|
|
711
|
+
/** Every behaviour of the auth family — the gate's and the account's — for a host's block registry. */
|
|
712
|
+
declare const BEHAVIOURS: {
|
|
713
|
+
'auth/accountPanelView': (inputs: Record<string, unknown>, config: Readonly<Record<string, unknown>>) => AccountPanelViewModel;
|
|
714
|
+
'auth/authGateView': (inputs: Record<string, unknown>, config: Readonly<Record<string, unknown>>) => AuthGateView;
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
export { BEHAVIOURS$1 as ACCOUNT_BEHAVIOURS, ACCOUNT_DECLARATION, ACCOUNT_PANEL_ID, AUTH_GATE_DECLARATION, AUTH_GATE_INITIAL, AUTH_GATE_PANEL_ID, AccountController, type AccountControllerOptions, type AccountHostBridge, type AccountPanelState, AccountPanelView, type AccountPanelViewModel, type AccountPanelViewOptions, type AccountPanelViewProps, type AccountRenderContext, type AccountUiIntent, type AccountViewState, type AuthGateAction, AuthGateController, type AuthGateHostBridge, AuthGateView$1 as AuthGateView, type AuthGateView as AuthGateViewModel, type AuthGateViewProps, BEHAVIOURS, type CreateAccountPanelOptions, type CreateAuthGatePanelOptions, type MountXenoAuthGateOptions, type MountedXenoAuthGate, PHASE_INTENTS, STATUS_LABEL, STATUS_TONE, STEP_UP_ACTIONS, type XenoAccountAction, type XenoAccountActionKind, type XenoAccountCredits, type XenoAccountIdentity, type XenoAccountPlan, type XenoAccountState, type XenoAccountStatus, type XenoAuthGateBridge, type XenoAuthGateIntent, type XenoAuthGateIntentKind, type XenoAuthGatePhase, type XenoAuthGateState, type XenoEntitlement, type XenoLedgerDelta, type XenoLedgerEntry, type XenoLicenceState, accountManifest, accountPanel, accountPanelView, accountViewManifest, authGateManifest, authGatePanel, authGateView, createAccountPanel, createAuthGatePanel, creditsAge, deriveAuthGatePhase, formatAge, formatAmount, formatSince, hostContradiction, intentOffered, isSignedIn, mountXenoAuthGate, plateCopy, requiresStepUp };
|
package/dist/auth/index.js
CHANGED
|
@@ -318,6 +318,365 @@ var AUTH_GATE_DECLARATION = {
|
|
|
318
318
|
}
|
|
319
319
|
};
|
|
320
320
|
|
|
321
|
+
// src/auth/account/manifest.ts
|
|
322
|
+
import { WELL_KNOWN_PORT_SCHEMAS } from "@xenosystem/block-sdk";
|
|
323
|
+
var ACCOUNT_PANEL_ID = "xeno.core.account";
|
|
324
|
+
var accountManifest = {
|
|
325
|
+
id: ACCOUNT_PANEL_ID,
|
|
326
|
+
version: "0.1.0",
|
|
327
|
+
title: "Account",
|
|
328
|
+
icon: "user-round",
|
|
329
|
+
description: "Identity, plan, credits, entitlements and ledger \u2014 all host-pushed, never fetched. Balances carry the moment they were true. Sensitive actions are emitted as step-up intents, never performed.",
|
|
330
|
+
defaultSlot: "inspector",
|
|
331
|
+
inputs: [
|
|
332
|
+
{
|
|
333
|
+
id: "account",
|
|
334
|
+
name: "Account",
|
|
335
|
+
type: "object",
|
|
336
|
+
schema: WELL_KNOWN_PORT_SCHEMAS.ACCOUNT,
|
|
337
|
+
description: "{status, identity?, plan?, credits?, entitlements?, seats?, message?}. The host is authoritative. Contains NO token \u2014 see AUTH L9.",
|
|
338
|
+
multiple: false
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
id: "ledger",
|
|
342
|
+
name: "Ledger",
|
|
343
|
+
type: "object",
|
|
344
|
+
schema: WELL_KNOWN_PORT_SCHEMAS.LEDGER,
|
|
345
|
+
description: "{rev?, append?, replace?, clear?, hasMore?}. Append-oriented; `rev` gates out-of-order deliveries.",
|
|
346
|
+
multiple: true
|
|
347
|
+
}
|
|
348
|
+
],
|
|
349
|
+
outputs: [
|
|
350
|
+
{
|
|
351
|
+
id: "action",
|
|
352
|
+
name: "Action",
|
|
353
|
+
type: "object",
|
|
354
|
+
schema: WELL_KNOWN_PORT_SCHEMAS.ACCOUNT_ACTION,
|
|
355
|
+
description: "{kind, seatId?, productId?, stepUp?}. An INTENT. `stepUp: true` marks an action AUTH L12 requires a fresh interactive re-auth for; the host must not satisfy it silently from the broker."
|
|
356
|
+
}
|
|
357
|
+
],
|
|
358
|
+
commands: [
|
|
359
|
+
{
|
|
360
|
+
id: "refresh",
|
|
361
|
+
title: "Refresh",
|
|
362
|
+
description: "Ask the host to re-read the balance. Idempotent; no step-up.",
|
|
363
|
+
parameters: {}
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
id: "get_account",
|
|
367
|
+
title: "Get Account",
|
|
368
|
+
description: "Return the rendered account state \u2014 status, plan, balance and its asOf. Never a token.",
|
|
369
|
+
parameters: {}
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
id: "get_ledger",
|
|
373
|
+
title: "Get Ledger",
|
|
374
|
+
description: "Return the held ledger entries, newest first.",
|
|
375
|
+
parameters: {}
|
|
376
|
+
}
|
|
377
|
+
// NOTE: `signOut`, `purchase` and `manageBilling` are deliberately NOT commands. They require a
|
|
378
|
+
// fresh interactive re-auth (AUTH L12), and an agent-invocable command is by definition not
|
|
379
|
+
// interactive. Exposing them would make this panel the bypass for the step-up requirement.
|
|
380
|
+
],
|
|
381
|
+
config: [
|
|
382
|
+
{
|
|
383
|
+
key: "showLedger",
|
|
384
|
+
label: "Show ledger",
|
|
385
|
+
type: "boolean",
|
|
386
|
+
defaultValue: true,
|
|
387
|
+
description: "Show recent credit activity."
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
key: "showEntitlements",
|
|
391
|
+
label: "Show entitlements",
|
|
392
|
+
type: "boolean",
|
|
393
|
+
defaultValue: true,
|
|
394
|
+
description: "List what the plan includes. Display only \u2014 entitlement is enforced server-side (AUTH L10)."
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
key: "staleAfterMs",
|
|
398
|
+
label: "Stale after (ms)",
|
|
399
|
+
type: "number",
|
|
400
|
+
defaultValue: 6e4,
|
|
401
|
+
description: "How old a balance may be before it is labelled stale."
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
key: "maxLedger",
|
|
405
|
+
label: "Max ledger entries",
|
|
406
|
+
type: "number",
|
|
407
|
+
defaultValue: 500,
|
|
408
|
+
description: "Entries retained in memory."
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
key: "emptyHint",
|
|
412
|
+
label: "Empty hint",
|
|
413
|
+
type: "text",
|
|
414
|
+
description: "Shown when no account state has arrived.",
|
|
415
|
+
placeholder: "Waiting for account state\u2026"
|
|
416
|
+
}
|
|
417
|
+
],
|
|
418
|
+
capabilities: ["storage.local"],
|
|
419
|
+
sdk: "^1.1.0"
|
|
420
|
+
};
|
|
421
|
+
|
|
422
|
+
// src/auth/account/types.ts
|
|
423
|
+
var STEP_UP_ACTIONS = /* @__PURE__ */ new Set([
|
|
424
|
+
"signOut",
|
|
425
|
+
"manageBilling",
|
|
426
|
+
"purchase"
|
|
427
|
+
]);
|
|
428
|
+
function isSignedIn(status) {
|
|
429
|
+
return status === "signedIn" || status === "refreshing" || status === "offline";
|
|
430
|
+
}
|
|
431
|
+
function requiresStepUp(kind) {
|
|
432
|
+
return STEP_UP_ACTIONS.has(kind);
|
|
433
|
+
}
|
|
434
|
+
function creditsAge(credits, now) {
|
|
435
|
+
if (!credits) return null;
|
|
436
|
+
return Math.max(0, now - credits.asOf);
|
|
437
|
+
}
|
|
438
|
+
function formatAmount(amount, unit = "") {
|
|
439
|
+
const sign = amount > 0 ? "+" : "";
|
|
440
|
+
const suffix = unit ? ` ${unit}` : "";
|
|
441
|
+
return `${sign}${amount.toLocaleString()}${suffix}`;
|
|
442
|
+
}
|
|
443
|
+
function formatAge(ms) {
|
|
444
|
+
if (ms < 6e4) return `${Math.round(ms / 1e3)}s ago`;
|
|
445
|
+
if (ms < 36e5) return `${Math.round(ms / 6e4)}m ago`;
|
|
446
|
+
if (ms < 864e5) return `${Math.round(ms / 36e5)}h ago`;
|
|
447
|
+
return `${Math.round(ms / 864e5)}d ago`;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/auth/account/declaration.ts
|
|
451
|
+
var accountViewManifest = {
|
|
452
|
+
id: ACCOUNT_PANEL_ID,
|
|
453
|
+
version: "1.0.0",
|
|
454
|
+
title: "Account",
|
|
455
|
+
category: "account",
|
|
456
|
+
layout: { slot: "side" },
|
|
457
|
+
inputs: [{ id: "view", type: "object", label: "View state", description: "The AccountViewState the controller derives from the host ports." }],
|
|
458
|
+
outputs: [{ id: "ui", type: "object", label: "UI intent", description: "{ kind, seatId?, collapsed? } \u2014 applied by the controller." }],
|
|
459
|
+
commands: []
|
|
460
|
+
};
|
|
461
|
+
var STATUS_LABEL = {
|
|
462
|
+
unknown: "Loading",
|
|
463
|
+
signedOut: "Signed out",
|
|
464
|
+
signedIn: "Signed in",
|
|
465
|
+
refreshing: "Refreshing",
|
|
466
|
+
expired: "Session expired",
|
|
467
|
+
offline: "Offline"
|
|
468
|
+
};
|
|
469
|
+
var STATUS_TONE = {
|
|
470
|
+
unknown: "neutral",
|
|
471
|
+
signedOut: "neutral",
|
|
472
|
+
signedIn: "success",
|
|
473
|
+
refreshing: "neutral",
|
|
474
|
+
expired: "warning",
|
|
475
|
+
offline: "warning"
|
|
476
|
+
};
|
|
477
|
+
function accountPanelView(state, options = {}) {
|
|
478
|
+
const showLedger = options.showLedger ?? true;
|
|
479
|
+
const showEntitlements = options.showEntitlements ?? true;
|
|
480
|
+
const { identity, plan, credits } = state;
|
|
481
|
+
const signedIn = state.status !== "unknown" && isSignedIn(state.status);
|
|
482
|
+
return {
|
|
483
|
+
loading: state.status === "unknown",
|
|
484
|
+
signedOut: state.status !== "unknown" && !isSignedIn(state.status),
|
|
485
|
+
signedIn,
|
|
486
|
+
loadingHint: options.emptyHint ?? "Waiting for account state\u2026",
|
|
487
|
+
statusLabel: STATUS_LABEL[state.status],
|
|
488
|
+
statusTone: STATUS_TONE[state.status],
|
|
489
|
+
signedOutHint: state.message ?? "Sign in to see your plan and credits.",
|
|
490
|
+
planLabel: plan?.label,
|
|
491
|
+
planTitle: plan ? `Plan: ${plan.label}` : void 0,
|
|
492
|
+
name: identity?.name ?? identity?.sub,
|
|
493
|
+
email: identity?.email,
|
|
494
|
+
message: state.message ?? void 0,
|
|
495
|
+
hasCredits: credits !== null,
|
|
496
|
+
creditsUnit: credits?.unit ?? "credits",
|
|
497
|
+
creditsBalance: credits ? credits.balance.toLocaleString() : "",
|
|
498
|
+
creditsSub: credits && state.creditsAgeMs !== null ? state.creditsStale ? `stale \u2014 as of ${formatAge(state.creditsAgeMs)}` : `as of ${formatAge(state.creditsAgeMs)}` : void 0,
|
|
499
|
+
creditsStale: state.creditsStale,
|
|
500
|
+
creditsTitle: "Pushed by the host, not fetched by this panel.",
|
|
501
|
+
hasIncluded: credits?.included !== void 0,
|
|
502
|
+
includedValue: credits?.included !== void 0 ? credits.included.toLocaleString() : "",
|
|
503
|
+
includedSub: credits?.used === void 0 ? void 0 : `${credits.used.toLocaleString()} used`,
|
|
504
|
+
showSeats: state.seats.length > 1,
|
|
505
|
+
seats: state.seats.map((s) => ({ id: s.id, label: s.label, active: !!s.active, inactive: !s.active })),
|
|
506
|
+
showEntitlements: showEntitlements && state.entitlements.length > 0,
|
|
507
|
+
entitlements: state.entitlements.map((e) => ({ id: e.id, label: e.label, detail: e.detail, tone: e.included ? "success" : "neutral", text: e.included ? "yes" : "no" })),
|
|
508
|
+
showLedger,
|
|
509
|
+
ledgerCollapsed: !state.ledgerExpanded,
|
|
510
|
+
ledgerCount: state.ledger.length > 0 ? String(state.ledger.length) : void 0,
|
|
511
|
+
hasLedger: state.ledger.length > 0,
|
|
512
|
+
ledgerEmpty: state.ledger.length === 0,
|
|
513
|
+
ledger: state.ledger.map((e) => ({ id: e.id, description: e.description, source: e.source, amount: formatAmount(e.amount) })),
|
|
514
|
+
ledgerHasMore: state.ledgerHasMore,
|
|
515
|
+
renews: plan?.renewsAt ? `renews ${new Date(plan.renewsAt).toLocaleDateString()}` : void 0,
|
|
516
|
+
offlineNote: state.status === "offline" ? "offline \u2014 showing last known" : void 0
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
var EMPTY = { status: "unknown", identity: null, plan: null, credits: null, entitlements: [], seats: [], message: null, ledger: [], ledgerHasMore: false, ledgerExpanded: false, creditsAgeMs: null, creditsStale: false };
|
|
520
|
+
var BEHAVIOURS2 = {
|
|
521
|
+
"auth/accountPanelView": (inputs, config) => accountPanelView(inputs.view ?? EMPTY, {
|
|
522
|
+
showLedger: typeof config.showLedger === "boolean" ? config.showLedger : void 0,
|
|
523
|
+
showEntitlements: typeof config.showEntitlements === "boolean" ? config.showEntitlements : void 0,
|
|
524
|
+
emptyHint: typeof config.emptyHint === "string" ? config.emptyHint : void 0
|
|
525
|
+
})
|
|
526
|
+
};
|
|
527
|
+
var ui = (kind) => ({ emit: "ui", value: { object: { kind: { value: kind } } } });
|
|
528
|
+
var text = (b) => ({ component: "text", props: { text: b } });
|
|
529
|
+
var badge = (b, when, props) => ({ component: "chrome/Badge", ...when ? { when } : {}, ...props ? { props } : {}, children: [text(b)] });
|
|
530
|
+
var ACCOUNT_DECLARATION = {
|
|
531
|
+
format: "xeno.block@1",
|
|
532
|
+
manifest: accountViewManifest,
|
|
533
|
+
behaviour: "auth/accountPanelView",
|
|
534
|
+
composition: {
|
|
535
|
+
component: "fragment",
|
|
536
|
+
children: [
|
|
537
|
+
{ component: "chrome/EmptyState", when: { input: "view.loading" }, props: { title: { value: "Account" }, hint: { input: "view.loadingHint" } } },
|
|
538
|
+
{
|
|
539
|
+
component: "chrome/EmptyState",
|
|
540
|
+
when: { input: "view.signedOut" },
|
|
541
|
+
props: {
|
|
542
|
+
title: { input: "view.statusLabel" },
|
|
543
|
+
hint: { input: "view.signedOutHint" },
|
|
544
|
+
action: { node: { component: "chrome/TextButton", props: { strong: { value: true } }, on: { onClick: ui("signIn") }, children: [text({ value: "Sign in" })] } }
|
|
545
|
+
}
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
component: "chrome/Column",
|
|
549
|
+
when: { input: "view.signedIn" },
|
|
550
|
+
children: [
|
|
551
|
+
{
|
|
552
|
+
component: "chrome/Toolbar",
|
|
553
|
+
props: {
|
|
554
|
+
left: { node: { component: "chrome/ToolbarGroup", children: [badge({ input: "view.statusLabel" }, void 0, { tone: { input: "view.statusTone" } }), badge({ input: "view.planLabel" }, { input: "view.planLabel" }, { title: { input: "view.planTitle" } })] } },
|
|
555
|
+
right: { node: { component: "chrome/ToolbarGroup", props: { end: { value: true } }, children: [{ component: "chrome/TextButton", on: { onClick: ui("refresh") }, children: [text({ value: "Refresh" })] }] } }
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
component: "chrome/ScrollArea",
|
|
560
|
+
children: [
|
|
561
|
+
{
|
|
562
|
+
component: "chrome/Stack",
|
|
563
|
+
children: [
|
|
564
|
+
{ component: "chrome/Text", props: { role: { value: "title" } }, children: [text({ input: "view.name" })] },
|
|
565
|
+
{ component: "chrome/Text", when: { input: "view.email" }, props: { role: { value: "meta" } }, children: [text({ input: "view.email" })] },
|
|
566
|
+
{ component: "chrome/Text", when: { input: "view.message" }, props: { role: { value: "meta" } }, children: [text({ input: "view.message" })] }
|
|
567
|
+
]
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
component: "chrome/Stack",
|
|
571
|
+
when: { input: "view.hasCredits" },
|
|
572
|
+
props: { direction: { value: "row" }, pad: { value: "inset" } },
|
|
573
|
+
children: [
|
|
574
|
+
{
|
|
575
|
+
component: "chrome/StatTile",
|
|
576
|
+
props: {
|
|
577
|
+
label: { input: "view.creditsUnit" },
|
|
578
|
+
value: { input: "view.creditsBalance" },
|
|
579
|
+
sub: { input: "view.creditsSub" },
|
|
580
|
+
icon: { node: badge({ value: "stale" }, { input: "view.creditsStale" }, { tone: { value: "warning" } }) },
|
|
581
|
+
title: { input: "view.creditsTitle" }
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
{ component: "chrome/StatTile", when: { input: "view.hasIncluded" }, props: { label: { value: "included" }, value: { input: "view.includedValue" }, sub: { input: "view.includedSub" } } }
|
|
585
|
+
]
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
component: "chrome/Section",
|
|
589
|
+
when: { input: "view.showSeats" },
|
|
590
|
+
props: { title: { value: "Seats" }, flush: { value: true } },
|
|
591
|
+
children: [
|
|
592
|
+
{
|
|
593
|
+
component: "chrome/RowList",
|
|
594
|
+
children: [
|
|
595
|
+
{
|
|
596
|
+
component: "chrome/Row",
|
|
597
|
+
each: { input: "view.seats", key: "id" },
|
|
598
|
+
props: {
|
|
599
|
+
noIcon: { value: true },
|
|
600
|
+
label: { item: "label" },
|
|
601
|
+
trailing: {
|
|
602
|
+
node: {
|
|
603
|
+
component: "fragment",
|
|
604
|
+
children: [
|
|
605
|
+
badge({ value: "active" }, { item: "active" }),
|
|
606
|
+
{ component: "chrome/TextButton", when: { item: "inactive" }, on: { onClick: { emit: "ui", value: { object: { kind: { value: "switchSeat" }, seatId: { item: "id" } } } } }, children: [text({ value: "Switch" })] }
|
|
607
|
+
]
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
]
|
|
613
|
+
}
|
|
614
|
+
]
|
|
615
|
+
},
|
|
616
|
+
{
|
|
617
|
+
component: "chrome/Section",
|
|
618
|
+
when: { input: "view.showEntitlements" },
|
|
619
|
+
props: { title: { value: "Included" }, flush: { value: true } },
|
|
620
|
+
children: [
|
|
621
|
+
{
|
|
622
|
+
component: "chrome/RowList",
|
|
623
|
+
children: [
|
|
624
|
+
{
|
|
625
|
+
component: "chrome/Row",
|
|
626
|
+
each: { input: "view.entitlements", key: "id" },
|
|
627
|
+
props: { noIcon: { value: true }, label: { item: "label" }, meta: { item: "detail" }, trailing: { node: badge({ item: "text" }, void 0, { tone: { item: "tone" } }) } }
|
|
628
|
+
}
|
|
629
|
+
]
|
|
630
|
+
}
|
|
631
|
+
]
|
|
632
|
+
},
|
|
633
|
+
{
|
|
634
|
+
component: "chrome/Section",
|
|
635
|
+
when: { input: "view.showLedger" },
|
|
636
|
+
props: { title: { value: "Recent activity" }, flush: { value: true }, collapsed: { input: "view.ledgerCollapsed" }, badge: { node: badge({ input: "view.ledgerCount" }, { input: "view.ledgerCount" }) } },
|
|
637
|
+
on: { onCollapsedChange: { emit: "ui", value: { object: { kind: { value: "ledgerCollapsed" }, collapsed: { arg: "" } } } } },
|
|
638
|
+
children: [
|
|
639
|
+
{ component: "chrome/EmptyState", when: { input: "view.ledgerEmpty" }, props: { title: { value: "No activity" }, hint: { value: "Credit spends and grants appear here." } } },
|
|
640
|
+
{
|
|
641
|
+
component: "chrome/RowList",
|
|
642
|
+
when: { input: "view.hasLedger" },
|
|
643
|
+
children: [
|
|
644
|
+
{
|
|
645
|
+
component: "chrome/Row",
|
|
646
|
+
each: { input: "view.ledger", key: "id" },
|
|
647
|
+
props: { noIcon: { value: true }, label: { item: "description" }, meta: { item: "source" }, trailing: { node: { component: "chrome/Text", props: { role: { value: "number" } }, children: [text({ item: "amount" })] } } }
|
|
648
|
+
}
|
|
649
|
+
]
|
|
650
|
+
},
|
|
651
|
+
{ component: "chrome/Stack", when: { input: "view.ledgerHasMore" }, props: { pad: { value: "inset" } }, children: [{ component: "chrome/Text", props: { role: { value: "caption" } }, children: [text({ value: "Older entries are not held by this panel." })] }] }
|
|
652
|
+
]
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
},
|
|
656
|
+
{ component: "chrome/Stack", props: { pad: { value: "inset" } }, children: [{ component: "chrome/Text", props: { role: { value: "caption" } }, children: [text({ value: "Billing and sign-out ask you to confirm who you are." })] }] },
|
|
657
|
+
{
|
|
658
|
+
component: "chrome/Toolbar",
|
|
659
|
+
props: {
|
|
660
|
+
divided: { value: false },
|
|
661
|
+
right: {
|
|
662
|
+
node: {
|
|
663
|
+
component: "chrome/ToolbarGroup",
|
|
664
|
+
props: { end: { value: true } },
|
|
665
|
+
children: [
|
|
666
|
+
{ component: "chrome/TextButton", on: { onClick: ui("manageBilling") }, children: [text({ value: "Billing\u2026" })] },
|
|
667
|
+
{ component: "chrome/TextButton", on: { onClick: ui("signOut") }, children: [text({ value: "Sign out\u2026" })] }
|
|
668
|
+
]
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
},
|
|
673
|
+
{ component: "chrome/StatusBar", props: { left: { input: "view.renews" }, right: { input: "view.offlineNote" } } }
|
|
674
|
+
]
|
|
675
|
+
}
|
|
676
|
+
]
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
|
|
321
680
|
// src/auth/gate/mount.ts
|
|
322
681
|
import { createElement as createElement2 } from "react";
|
|
323
682
|
import { createRoot as createRoot2 } from "react-dom/client";
|
|
@@ -355,34 +714,6 @@ import {
|
|
|
355
714
|
bindConfig as bindConfig2
|
|
356
715
|
} from "@xenosystem/block-sdk";
|
|
357
716
|
|
|
358
|
-
// src/auth/account/types.ts
|
|
359
|
-
var STEP_UP_ACTIONS = /* @__PURE__ */ new Set([
|
|
360
|
-
"signOut",
|
|
361
|
-
"manageBilling",
|
|
362
|
-
"purchase"
|
|
363
|
-
]);
|
|
364
|
-
function isSignedIn(status) {
|
|
365
|
-
return status === "signedIn" || status === "refreshing" || status === "offline";
|
|
366
|
-
}
|
|
367
|
-
function requiresStepUp(kind) {
|
|
368
|
-
return STEP_UP_ACTIONS.has(kind);
|
|
369
|
-
}
|
|
370
|
-
function creditsAge(credits, now) {
|
|
371
|
-
if (!credits) return null;
|
|
372
|
-
return Math.max(0, now - credits.asOf);
|
|
373
|
-
}
|
|
374
|
-
function formatAmount(amount, unit = "") {
|
|
375
|
-
const sign = amount > 0 ? "+" : "";
|
|
376
|
-
const suffix = unit ? ` ${unit}` : "";
|
|
377
|
-
return `${sign}${amount.toLocaleString()}${suffix}`;
|
|
378
|
-
}
|
|
379
|
-
function formatAge(ms) {
|
|
380
|
-
if (ms < 6e4) return `${Math.round(ms / 1e3)}s ago`;
|
|
381
|
-
if (ms < 36e5) return `${Math.round(ms / 6e4)}m ago`;
|
|
382
|
-
if (ms < 864e5) return `${Math.round(ms / 36e5)}h ago`;
|
|
383
|
-
return `${Math.round(ms / 864e5)}d ago`;
|
|
384
|
-
}
|
|
385
|
-
|
|
386
717
|
// src/auth/account/controller.ts
|
|
387
718
|
var EMPTY_STATE = { status: "unknown" };
|
|
388
719
|
function ageBucket(ms) {
|
|
@@ -593,139 +924,25 @@ var AccountController = class {
|
|
|
593
924
|
}
|
|
594
925
|
};
|
|
595
926
|
|
|
596
|
-
// src/auth/account/manifest.ts
|
|
597
|
-
import { WELL_KNOWN_PORT_SCHEMAS } from "@xenosystem/block-sdk";
|
|
598
|
-
var ACCOUNT_PANEL_ID = "xeno.core.account";
|
|
599
|
-
var accountManifest = {
|
|
600
|
-
id: ACCOUNT_PANEL_ID,
|
|
601
|
-
version: "0.1.0",
|
|
602
|
-
title: "Account",
|
|
603
|
-
icon: "user-round",
|
|
604
|
-
description: "Identity, plan, credits, entitlements and ledger \u2014 all host-pushed, never fetched. Balances carry the moment they were true. Sensitive actions are emitted as step-up intents, never performed.",
|
|
605
|
-
defaultSlot: "inspector",
|
|
606
|
-
inputs: [
|
|
607
|
-
{
|
|
608
|
-
id: "account",
|
|
609
|
-
name: "Account",
|
|
610
|
-
type: "object",
|
|
611
|
-
schema: WELL_KNOWN_PORT_SCHEMAS.ACCOUNT,
|
|
612
|
-
description: "{status, identity?, plan?, credits?, entitlements?, seats?, message?}. The host is authoritative. Contains NO token \u2014 see AUTH L9.",
|
|
613
|
-
multiple: false
|
|
614
|
-
},
|
|
615
|
-
{
|
|
616
|
-
id: "ledger",
|
|
617
|
-
name: "Ledger",
|
|
618
|
-
type: "object",
|
|
619
|
-
schema: WELL_KNOWN_PORT_SCHEMAS.LEDGER,
|
|
620
|
-
description: "{rev?, append?, replace?, clear?, hasMore?}. Append-oriented; `rev` gates out-of-order deliveries.",
|
|
621
|
-
multiple: true
|
|
622
|
-
}
|
|
623
|
-
],
|
|
624
|
-
outputs: [
|
|
625
|
-
{
|
|
626
|
-
id: "action",
|
|
627
|
-
name: "Action",
|
|
628
|
-
type: "object",
|
|
629
|
-
schema: WELL_KNOWN_PORT_SCHEMAS.ACCOUNT_ACTION,
|
|
630
|
-
description: "{kind, seatId?, productId?, stepUp?}. An INTENT. `stepUp: true` marks an action AUTH L12 requires a fresh interactive re-auth for; the host must not satisfy it silently from the broker."
|
|
631
|
-
}
|
|
632
|
-
],
|
|
633
|
-
commands: [
|
|
634
|
-
{
|
|
635
|
-
id: "refresh",
|
|
636
|
-
title: "Refresh",
|
|
637
|
-
description: "Ask the host to re-read the balance. Idempotent; no step-up.",
|
|
638
|
-
parameters: {}
|
|
639
|
-
},
|
|
640
|
-
{
|
|
641
|
-
id: "get_account",
|
|
642
|
-
title: "Get Account",
|
|
643
|
-
description: "Return the rendered account state \u2014 status, plan, balance and its asOf. Never a token.",
|
|
644
|
-
parameters: {}
|
|
645
|
-
},
|
|
646
|
-
{
|
|
647
|
-
id: "get_ledger",
|
|
648
|
-
title: "Get Ledger",
|
|
649
|
-
description: "Return the held ledger entries, newest first.",
|
|
650
|
-
parameters: {}
|
|
651
|
-
}
|
|
652
|
-
// NOTE: `signOut`, `purchase` and `manageBilling` are deliberately NOT commands. They require a
|
|
653
|
-
// fresh interactive re-auth (AUTH L12), and an agent-invocable command is by definition not
|
|
654
|
-
// interactive. Exposing them would make this panel the bypass for the step-up requirement.
|
|
655
|
-
],
|
|
656
|
-
config: [
|
|
657
|
-
{
|
|
658
|
-
key: "showLedger",
|
|
659
|
-
label: "Show ledger",
|
|
660
|
-
type: "boolean",
|
|
661
|
-
defaultValue: true,
|
|
662
|
-
description: "Show recent credit activity."
|
|
663
|
-
},
|
|
664
|
-
{
|
|
665
|
-
key: "showEntitlements",
|
|
666
|
-
label: "Show entitlements",
|
|
667
|
-
type: "boolean",
|
|
668
|
-
defaultValue: true,
|
|
669
|
-
description: "List what the plan includes. Display only \u2014 entitlement is enforced server-side (AUTH L10)."
|
|
670
|
-
},
|
|
671
|
-
{
|
|
672
|
-
key: "staleAfterMs",
|
|
673
|
-
label: "Stale after (ms)",
|
|
674
|
-
type: "number",
|
|
675
|
-
defaultValue: 6e4,
|
|
676
|
-
description: "How old a balance may be before it is labelled stale."
|
|
677
|
-
},
|
|
678
|
-
{
|
|
679
|
-
key: "maxLedger",
|
|
680
|
-
label: "Max ledger entries",
|
|
681
|
-
type: "number",
|
|
682
|
-
defaultValue: 500,
|
|
683
|
-
description: "Entries retained in memory."
|
|
684
|
-
},
|
|
685
|
-
{
|
|
686
|
-
key: "emptyHint",
|
|
687
|
-
label: "Empty hint",
|
|
688
|
-
type: "text",
|
|
689
|
-
description: "Shown when no account state has arrived.",
|
|
690
|
-
placeholder: "Waiting for account state\u2026"
|
|
691
|
-
}
|
|
692
|
-
],
|
|
693
|
-
capabilities: ["storage.local"],
|
|
694
|
-
sdk: "^1.1.0"
|
|
695
|
-
};
|
|
696
|
-
|
|
697
927
|
// src/auth/account/react/AccountPanelView.tsx
|
|
698
928
|
import { useSyncExternalStore } from "react";
|
|
699
929
|
import {
|
|
700
930
|
Badge,
|
|
931
|
+
Column,
|
|
701
932
|
EmptyState,
|
|
702
933
|
Row,
|
|
703
934
|
RowList,
|
|
704
935
|
ScrollArea,
|
|
705
936
|
Section,
|
|
706
937
|
StatTile,
|
|
938
|
+
Stack,
|
|
707
939
|
StatusBar,
|
|
940
|
+
Text,
|
|
708
941
|
TextButton,
|
|
709
942
|
Toolbar,
|
|
710
943
|
ToolbarGroup
|
|
711
944
|
} from "@xenosystem/components/chrome";
|
|
712
945
|
import { Fragment as Fragment2, jsx as jsx2, jsxs } from "react/jsx-runtime";
|
|
713
|
-
var STATUS_LABEL = {
|
|
714
|
-
unknown: "Loading",
|
|
715
|
-
signedOut: "Signed out",
|
|
716
|
-
signedIn: "Signed in",
|
|
717
|
-
refreshing: "Refreshing",
|
|
718
|
-
expired: "Session expired",
|
|
719
|
-
offline: "Offline"
|
|
720
|
-
};
|
|
721
|
-
var STATUS_TONE = {
|
|
722
|
-
unknown: "neutral",
|
|
723
|
-
signedOut: "neutral",
|
|
724
|
-
signedIn: "success",
|
|
725
|
-
refreshing: "neutral",
|
|
726
|
-
expired: "warning",
|
|
727
|
-
offline: "warning"
|
|
728
|
-
};
|
|
729
946
|
function AccountPanelView({
|
|
730
947
|
controller,
|
|
731
948
|
showLedger = true,
|
|
@@ -733,108 +950,81 @@ function AccountPanelView({
|
|
|
733
950
|
emptyHint
|
|
734
951
|
}) {
|
|
735
952
|
const state = useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
if (!isSignedIn(state.status)) {
|
|
953
|
+
const v = accountPanelView(state, { showLedger, showEntitlements, emptyHint });
|
|
954
|
+
if (v.loading) return /* @__PURE__ */ jsx2(EmptyState, { title: "Account", hint: v.loadingHint });
|
|
955
|
+
if (v.signedOut) {
|
|
740
956
|
return /* @__PURE__ */ jsx2(
|
|
741
957
|
EmptyState,
|
|
742
958
|
{
|
|
743
|
-
title:
|
|
744
|
-
hint:
|
|
959
|
+
title: v.statusLabel,
|
|
960
|
+
hint: v.signedOutHint,
|
|
745
961
|
action: /* @__PURE__ */ jsx2(TextButton, { strong: true, onClick: () => controller.request("signIn"), children: "Sign in" })
|
|
746
962
|
}
|
|
747
963
|
);
|
|
748
964
|
}
|
|
749
|
-
|
|
750
|
-
const unit = credits?.unit ?? "credits";
|
|
751
|
-
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", minHeight: 0 }, children: [
|
|
965
|
+
return /* @__PURE__ */ jsxs(Column, { children: [
|
|
752
966
|
/* @__PURE__ */ jsx2(
|
|
753
967
|
Toolbar,
|
|
754
968
|
{
|
|
755
|
-
left: /* @__PURE__ */ jsxs(
|
|
756
|
-
/* @__PURE__ */ jsx2(Badge, { tone:
|
|
757
|
-
|
|
969
|
+
left: /* @__PURE__ */ jsxs(ToolbarGroup, { children: [
|
|
970
|
+
/* @__PURE__ */ jsx2(Badge, { tone: v.statusTone, children: v.statusLabel }),
|
|
971
|
+
v.planLabel ? /* @__PURE__ */ jsx2(Badge, { title: v.planTitle, children: v.planLabel }) : null
|
|
758
972
|
] }),
|
|
759
973
|
right: /* @__PURE__ */ jsx2(ToolbarGroup, { end: true, children: /* @__PURE__ */ jsx2(TextButton, { onClick: () => controller.refresh(), children: "Refresh" }) })
|
|
760
974
|
}
|
|
761
975
|
),
|
|
762
976
|
/* @__PURE__ */ jsxs(ScrollArea, { children: [
|
|
763
|
-
/* @__PURE__ */ jsxs(
|
|
764
|
-
/* @__PURE__ */ jsx2(
|
|
765
|
-
|
|
766
|
-
|
|
977
|
+
/* @__PURE__ */ jsxs(Stack, { children: [
|
|
978
|
+
/* @__PURE__ */ jsx2(Text, { role: "title", children: v.name }),
|
|
979
|
+
v.email ? /* @__PURE__ */ jsx2(Text, { role: "meta", children: v.email }) : null,
|
|
980
|
+
v.message ? /* @__PURE__ */ jsx2(Text, { role: "meta", children: v.message }) : null
|
|
767
981
|
] }),
|
|
768
|
-
|
|
982
|
+
v.hasCredits ? /* @__PURE__ */ jsxs(Stack, { direction: "row", pad: "inset", children: [
|
|
769
983
|
/* @__PURE__ */ jsx2(
|
|
770
984
|
StatTile,
|
|
771
985
|
{
|
|
772
|
-
label:
|
|
773
|
-
value:
|
|
774
|
-
sub:
|
|
775
|
-
icon:
|
|
776
|
-
title:
|
|
986
|
+
label: v.creditsUnit,
|
|
987
|
+
value: v.creditsBalance,
|
|
988
|
+
sub: v.creditsSub,
|
|
989
|
+
icon: v.creditsStale ? /* @__PURE__ */ jsx2(Badge, { tone: "warning", children: "stale" }) : null,
|
|
990
|
+
title: v.creditsTitle
|
|
777
991
|
}
|
|
778
992
|
),
|
|
779
|
-
|
|
780
|
-
StatTile,
|
|
781
|
-
{
|
|
782
|
-
label: "included",
|
|
783
|
-
value: credits.included.toLocaleString(),
|
|
784
|
-
sub: credits.used === void 0 ? void 0 : `${credits.used.toLocaleString()} used`
|
|
785
|
-
}
|
|
786
|
-
) : null
|
|
993
|
+
v.hasIncluded ? /* @__PURE__ */ jsx2(StatTile, { label: "included", value: v.includedValue, sub: v.includedSub }) : null
|
|
787
994
|
] }) : null,
|
|
788
|
-
|
|
995
|
+
v.showSeats ? /* @__PURE__ */ jsx2(Section, { title: "Seats", flush: true, children: /* @__PURE__ */ jsx2(RowList, { children: v.seats.map((seat) => /* @__PURE__ */ jsx2(
|
|
789
996
|
Row,
|
|
790
997
|
{
|
|
791
998
|
noIcon: true,
|
|
792
999
|
label: seat.label,
|
|
793
|
-
trailing:
|
|
794
|
-
|
|
795
|
-
{
|
|
796
|
-
|
|
797
|
-
children: "Switch"
|
|
798
|
-
}
|
|
799
|
-
)
|
|
1000
|
+
trailing: /* @__PURE__ */ jsxs(Fragment2, { children: [
|
|
1001
|
+
seat.active ? /* @__PURE__ */ jsx2(Badge, { children: "active" }) : null,
|
|
1002
|
+
seat.inactive ? /* @__PURE__ */ jsx2(TextButton, { onClick: () => controller.request("switchSeat", { seatId: seat.id }), children: "Switch" }) : null
|
|
1003
|
+
] })
|
|
800
1004
|
},
|
|
801
1005
|
seat.id
|
|
802
1006
|
)) }) }) : null,
|
|
803
|
-
showEntitlements
|
|
804
|
-
|
|
805
|
-
{
|
|
806
|
-
noIcon: true,
|
|
807
|
-
label: ent.label,
|
|
808
|
-
meta: ent.detail,
|
|
809
|
-
trailing: /* @__PURE__ */ jsx2(Badge, { tone: ent.included ? "success" : "neutral", children: ent.included ? "yes" : "no" })
|
|
810
|
-
},
|
|
811
|
-
ent.id
|
|
1007
|
+
v.showEntitlements ? /* @__PURE__ */ jsx2(Section, { title: "Included", flush: true, children: /* @__PURE__ */ jsx2(RowList, { children: v.entitlements.map((ent) => (
|
|
1008
|
+
// A fact about the plan, not a permission check. The gate is server-side (L10).
|
|
1009
|
+
/* @__PURE__ */ jsx2(Row, { noIcon: true, label: ent.label, meta: ent.detail, trailing: /* @__PURE__ */ jsx2(Badge, { tone: ent.tone, children: ent.text }) }, ent.id)
|
|
812
1010
|
)) }) }) : null,
|
|
813
|
-
showLedger ? /* @__PURE__ */ jsxs(
|
|
1011
|
+
v.showLedger ? /* @__PURE__ */ jsxs(
|
|
814
1012
|
Section,
|
|
815
1013
|
{
|
|
816
1014
|
title: "Recent activity",
|
|
817
1015
|
flush: true,
|
|
818
|
-
collapsed:
|
|
1016
|
+
collapsed: v.ledgerCollapsed,
|
|
819
1017
|
onCollapsedChange: (collapsed) => controller.setLedgerExpanded(!collapsed),
|
|
820
|
-
badge:
|
|
1018
|
+
badge: v.ledgerCount ? /* @__PURE__ */ jsx2(Badge, { children: v.ledgerCount }) : null,
|
|
821
1019
|
children: [
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
noIcon: true,
|
|
826
|
-
label: entry.description,
|
|
827
|
-
meta: entry.source,
|
|
828
|
-
trailing: /* @__PURE__ */ jsx2("span", { style: { fontVariantNumeric: "tabular-nums", fontSize: 11 }, children: formatAmount(entry.amount) })
|
|
829
|
-
},
|
|
830
|
-
entry.id
|
|
831
|
-
)) }),
|
|
832
|
-
state.ledgerHasMore ? /* @__PURE__ */ jsx2("div", { style: { padding: "2px 8px", fontSize: 9, opacity: 0.6 }, children: "Older entries are not held by this panel." }) : null
|
|
1020
|
+
v.ledgerEmpty ? /* @__PURE__ */ jsx2(EmptyState, { title: "No activity", hint: "Credit spends and grants appear here." }) : null,
|
|
1021
|
+
v.hasLedger ? /* @__PURE__ */ jsx2(RowList, { children: v.ledger.map((entry) => /* @__PURE__ */ jsx2(Row, { noIcon: true, label: entry.description, meta: entry.source, trailing: /* @__PURE__ */ jsx2(Text, { role: "number", children: entry.amount }) }, entry.id)) }) : null,
|
|
1022
|
+
v.ledgerHasMore ? /* @__PURE__ */ jsx2(Stack, { pad: "inset", children: /* @__PURE__ */ jsx2(Text, { role: "caption", children: "Older entries are not held by this panel." }) }) : null
|
|
833
1023
|
]
|
|
834
1024
|
}
|
|
835
1025
|
) : null
|
|
836
1026
|
] }),
|
|
837
|
-
/* @__PURE__ */ jsx2(
|
|
1027
|
+
/* @__PURE__ */ jsx2(Stack, { pad: "inset", children: /* @__PURE__ */ jsx2(Text, { role: "caption", children: "Billing and sign-out ask you to confirm who you are." }) }),
|
|
838
1028
|
/* @__PURE__ */ jsx2(
|
|
839
1029
|
Toolbar,
|
|
840
1030
|
{
|
|
@@ -845,13 +1035,7 @@ function AccountPanelView({
|
|
|
845
1035
|
] })
|
|
846
1036
|
}
|
|
847
1037
|
),
|
|
848
|
-
/* @__PURE__ */ jsx2(
|
|
849
|
-
StatusBar,
|
|
850
|
-
{
|
|
851
|
-
left: plan?.renewsAt ? `renews ${new Date(plan.renewsAt).toLocaleDateString()}` : void 0,
|
|
852
|
-
right: state.status === "offline" ? "offline \u2014 showing last known" : void 0
|
|
853
|
-
}
|
|
854
|
-
)
|
|
1038
|
+
/* @__PURE__ */ jsx2(StatusBar, { left: v.renews, right: v.offlineNote })
|
|
855
1039
|
] });
|
|
856
1040
|
}
|
|
857
1041
|
|
|
@@ -955,7 +1139,12 @@ function createAccountPanel(options = {}) {
|
|
|
955
1139
|
};
|
|
956
1140
|
}
|
|
957
1141
|
var accountPanel = createAccountPanel();
|
|
1142
|
+
|
|
1143
|
+
// src/auth/index.ts
|
|
1144
|
+
var BEHAVIOURS3 = { ...BEHAVIOURS, ...BEHAVIOURS2 };
|
|
958
1145
|
export {
|
|
1146
|
+
BEHAVIOURS2 as ACCOUNT_BEHAVIOURS,
|
|
1147
|
+
ACCOUNT_DECLARATION,
|
|
959
1148
|
ACCOUNT_PANEL_ID,
|
|
960
1149
|
AUTH_GATE_DECLARATION,
|
|
961
1150
|
AUTH_GATE_INITIAL,
|
|
@@ -964,11 +1153,15 @@ export {
|
|
|
964
1153
|
AccountPanelView,
|
|
965
1154
|
AuthGateController,
|
|
966
1155
|
AuthGateView,
|
|
967
|
-
BEHAVIOURS,
|
|
1156
|
+
BEHAVIOURS3 as BEHAVIOURS,
|
|
968
1157
|
PHASE_INTENTS,
|
|
1158
|
+
STATUS_LABEL,
|
|
1159
|
+
STATUS_TONE,
|
|
969
1160
|
STEP_UP_ACTIONS,
|
|
970
1161
|
accountManifest,
|
|
971
1162
|
accountPanel,
|
|
1163
|
+
accountPanelView,
|
|
1164
|
+
accountViewManifest,
|
|
972
1165
|
authGateManifest,
|
|
973
1166
|
authGatePanel,
|
|
974
1167
|
authGateView,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenosystem/blocks",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "XENO Blocks — rung 3 of the front ladder: components composed into a contract-bearing unit (manifest: ports, intents, commands, agent surface), rendered by @xenosystem/workbench. ONE package, one subpath per family (/auth, /edit, /data, /time, /ops, /trust, /agent), one named export per block — XENO PACKAGE NAMING - STANDARD.md.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"peerDependencies": {
|
|
61
61
|
"@duckdb/duckdb-wasm": ">=1.28.0",
|
|
62
62
|
"@xenosystem/block-sdk": "^0.3.0",
|
|
63
|
-
"@xenosystem/components": ">=0.9.
|
|
63
|
+
"@xenosystem/components": ">=0.9.3 <1",
|
|
64
64
|
"@xenosystem/data-core": "^0.1.0",
|
|
65
65
|
"@xenosystem/elements-react": ">=0.0.1",
|
|
66
66
|
"@xenosystem/time-core": "^0.1.0",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"@types/react": "^19.0.0",
|
|
77
77
|
"@types/react-dom": "^19.0.0",
|
|
78
78
|
"@xenosystem/block-sdk": "^0.3.1",
|
|
79
|
-
"@xenosystem/components": "^0.9.
|
|
79
|
+
"@xenosystem/components": "^0.9.3",
|
|
80
80
|
"@xenosystem/data-core": "^0.1.0",
|
|
81
81
|
"@xenosystem/elements-react": "0.0.1",
|
|
82
82
|
"@xenosystem/time-core": "^0.1.0",
|