@wildmason/aegis 1.16.0 → 2.0.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 CHANGED
@@ -18,7 +18,116 @@ that 404s.
18
18
 
19
19
  ## [Unreleased]
20
20
 
21
- _No unreleased changes._
21
+ ## [2.0.0] - 2026-09-15
22
+
23
+ Major version because two exports go away: the license UI leaves
24
+ `@wildmason/aegis/components`, and `.wm-kbd--on-accent` is removed. A consumer on
25
+ a `^1.x` range does not pick this release up by accident.
26
+
27
+ ### Removed
28
+
29
+ - **BREAKING:** `.wm-kbd--on-accent`, deprecated in 1.17.0 and slated for removal
30
+ here. Use `.wm-kbd--on-fill`, which carries the same declarations. Helm, Mortar,
31
+ Works, Hearth's Workbench and the docs app used neither name at removal.
32
+
33
+ ### Changed
34
+
35
+ - **BREAKING:** the license UI moved to its own entry point,
36
+ `@wildmason/aegis/license`. `WmLicenseService`, `WmLicenseNudge`,
37
+ `WmLicenseDialog`, `LicenseStatus` and `WmDeclaredUseType` are no longer
38
+ exported from `@wildmason/aegis/components`. Change the import path; nothing
39
+ else about them changed.
40
+
41
+ ```ts
42
+ // before
43
+ import { WmLicenseService, WmLicenseNudge } from '@wildmason/aegis/components';
44
+ // after
45
+ import { WmLicenseService, WmLicenseNudge } from '@wildmason/aegis/license';
46
+ ```
47
+
48
+ The reason is Tauri. The license service calls Tauri commands, and while it
49
+ shared one bundle with every other component, every consumer imported
50
+ `@tauri-apps/api/core`. npm installed it as a required peer, and a web app —
51
+ Hearth's Workbench, measured — kept 1,682 bytes of Tauri IPC code it could
52
+ never call, because that package declares no `sideEffects` and esbuild could
53
+ not prove the import droppable. `@wildmason/aegis/components` now imports
54
+ nothing from Tauri.
55
+ - `@tauri-apps/api` is an **optional** peer. Only `@wildmason/aegis/license`
56
+ needs it, and a Tauri app that uses the license UI already depends on it.
57
+ - `package.json` exports `"."` as well as `"./components"`, both pointing at the
58
+ same bundle. The license bundle imports the shared components by package name,
59
+ so `WmModal`, `WmButton` and `WmToastService` stay single classes rather than
60
+ being copied into a second bundle.
61
+
62
+ ### Security
63
+
64
+ - Dev and build dependencies updated to clear `npm audit`: Angular 21.2.23,
65
+ ng-packagr 21.2.7 and the transitive packages under them for the library, and
66
+ Angular 21.2.23, `@angular/build`/`@angular/cli` 21.2.24 and Vitest 4.1.11 for
67
+ the docs app. Both now report 0 vulnerabilities. The published package ships no
68
+ runtime dependencies, so consumers inherited none of these; the peer ranges are
69
+ unchanged.
70
+
71
+ ### Fixed
72
+
73
+ - The Navigation & Tree Patterns guide page was routed in the docs app but
74
+ missing from `mcp/guide-index.json`, so the MCP server never served it. It is
75
+ indexed now.
76
+ - `DESIGN_LANGUAGE.md` said the package has two entry points and listed three.
77
+ It now lists all four, including `@wildmason/aegis/license`, and the Getting
78
+ Started page shows both import paths.
79
+
80
+ ### Added
81
+
82
+ - A fourth `check:docs` rule: every guide page the docs app routes must be in
83
+ `mcp/guide-index.json`, every indexed slug must be routed, and every indexed
84
+ `htmlPath` must exist. The MCP server skips a missing page with a warning
85
+ nobody sees, which is how the Navigation page went unserved.
86
+ - `check:entry-points`, run by `npm run build` after `ng-packagr`. It fails the
87
+ build if the main bundle imports from `@tauri-apps/` or exports a license
88
+ symbol, if the license bundle is incomplete or carries its own copy of a
89
+ shared component, if any `exports` target does not resolve, or if the Tauri
90
+ peer stops being optional.
91
+
92
+ ## [1.18.0] - 2026-09-02
93
+
94
+ ### Added
95
+
96
+ - `WmRichTooltip` — a peer to the text-only `WmTooltip` for generic Angular
97
+ markup. Consumers project a trigger marked `wmRichTooltipTrigger` and an
98
+ `ng-template` marked `wmRichTooltipContent`; the template renders in the
99
+ same anchored overlay shape with focus, hover, Escape, ARIA, scroll, and
100
+ clipping behavior. It accepts no raw HTML string, leaving any explicit
101
+ `[innerHTML]` sanitization decision with the consumer. Helm's Stacks
102
+ portfolio uses it to pair full branch refs and state swatches with the
103
+ topology legend, including omitted overflow branches.
104
+
105
+ ## [1.17.0] - 2026-09-01
106
+
107
+ ### Added
108
+
109
+ - `.wm-kbd--on-fill` — the same key cap `.wm-kbd--on-accent` shipped as one
110
+ release ago, driven by `currentColor` instead of `--wm-color-accent-text` so
111
+ it is correct on any control that carries its own fill rather than on the
112
+ accent alone. On `.wm-btn--primary` `currentColor` **is**
113
+ `--wm-color-accent-text`, so the accent case computes to exactly what 1.16.0
114
+ produced and `check:accent-contrast` measures the same numbers.
115
+
116
+ The generality was not speculative. Helm's conflict-resolution row puts a cap
117
+ inside a Reject button filled with `--wm-color-danger` at 22% over a tinted
118
+ header. Measured on all twelve themes, a plain `.wm-kbd` there draws an edge
119
+ that separates from the button at **1.65:1 to 2.89:1 — below WCAG 1.4.11's
120
+ 3:1 on every one of them**. The same cap under this modifier measures 3.55:1
121
+ to 4.43:1. Nothing about that button is unusual; the accent-only modifier
122
+ simply had no answer for it.
123
+
124
+ ### Deprecated
125
+
126
+ - `.wm-kbd--on-accent`, in favour of `.wm-kbd--on-fill`. It still ships, as the
127
+ same declarations, because removing a published class is a breaking change —
128
+ and it had no consumers to break, which is precisely why the name was worth
129
+ correcting one release later rather than living with. Slated for removal at
130
+ 2.0.0.
22
131
 
23
132
  ## [1.16.0] - 2026-09-01
24
133
 
@@ -302,7 +411,9 @@ _No unreleased changes._
302
411
  Releases before 1.8.0 are in `git log`. See also `wiki/products/Aegis - Health.md`
303
412
  for the decisions behind these changes, including the ones decided against.
304
413
 
305
- [Unreleased]: https://github.com/wildmason/aegis/compare/v1.16.0...HEAD
414
+ [Unreleased]: https://github.com/wildmason/aegis/compare/v1.18.0...HEAD
415
+ [1.18.0]: https://github.com/wildmason/aegis/releases/tag/v1.18.0
416
+ [1.17.0]: https://github.com/wildmason/aegis/releases/tag/v1.17.0
306
417
  [1.16.0]: https://github.com/wildmason/aegis/releases/tag/v1.16.0
307
418
  [1.15.0]: https://github.com/wildmason/aegis/releases/tag/v1.15.0
308
419
  [1.11.0]: https://github.com/wildmason/aegis/releases/tag/v1.11.0
@@ -0,0 +1,153 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, signal, computed, Injectable, input, output, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { invoke } from '@tauri-apps/api/core';
4
+ import { WmToastService, WmButton, WmModal } from '@wildmason/aegis';
5
+
6
+ class WmLicenseService {
7
+ toast = inject(WmToastService);
8
+ license = signal(null, ...(ngDevMode ? [{ debugName: "license" }] : /* istanbul ignore next */ []));
9
+ isLicensed = computed(() => this.license()?.valid ?? false, ...(ngDevMode ? [{ debugName: "isLicensed" }] : /* istanbul ignore next */ []));
10
+ orgName = computed(() => this.license()?.org ?? null, ...(ngDevMode ? [{ debugName: "orgName" }] : /* istanbul ignore next */ []));
11
+ async checkLicense() {
12
+ try {
13
+ const status = await invoke('get_license_status');
14
+ this.license.set(status);
15
+ if (!status.valid && status.error) {
16
+ const message = status.error === 'expired'
17
+ ? 'Your license key has expired.'
18
+ : 'Your license key is not valid.';
19
+ this.toast.error(message);
20
+ }
21
+ }
22
+ catch (e) {
23
+ console.error('License check failed:', e);
24
+ this.license.set(null);
25
+ }
26
+ }
27
+ async installKey(jwt) {
28
+ const status = await invoke('install_license', { key: jwt });
29
+ this.license.set(status);
30
+ return status;
31
+ }
32
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
33
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseService, providedIn: 'root' });
34
+ }
35
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseService, decorators: [{
36
+ type: Injectable,
37
+ args: [{ providedIn: 'root' }]
38
+ }] });
39
+
40
+ class WmLicenseNudge {
41
+ licenseService = inject(WmLicenseService);
42
+ /**
43
+ * Self-declared use type from the host app's first-run modal. When the
44
+ * user has declared `personal` or `oss` the nudge stays hidden — they've
45
+ * already told us a commercial key isn't relevant. `commercial` (without
46
+ * a key) and `null` (no declaration yet) keep the nudge visible.
47
+ *
48
+ * Typed `string | null` rather than `WmDeclaredUseType | null` because
49
+ * ng-packagr's partial-compilation d.ts emit drops the `null` from a
50
+ * union with a string-literal type, which then forces every consumer to
51
+ * `as` the binding. Runtime narrowing happens in `shouldShow`.
52
+ */
53
+ declaredUseType = input(null, ...(ngDevMode ? [{ debugName: "declaredUseType" }] : /* istanbul ignore next */ []));
54
+ dismissed = signal(false, ...(ngDevMode ? [{ debugName: "dismissed" }] : /* istanbul ignore next */ []));
55
+ enterLicense = output();
56
+ shouldShow = () => {
57
+ if (this.licenseService.isLicensed())
58
+ return false;
59
+ if (this.dismissed())
60
+ return false;
61
+ const ut = this.declaredUseType();
62
+ if (ut === 'personal' || ut === 'oss')
63
+ return false;
64
+ return true;
65
+ };
66
+ dismiss() {
67
+ this.dismissed.set(true);
68
+ }
69
+ onEnterLicense() {
70
+ this.enterLicense.emit();
71
+ }
72
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseNudge, deps: [], target: i0.ɵɵFactoryTarget.Component });
73
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.23", type: WmLicenseNudge, isStandalone: true, selector: "wm-license-nudge", inputs: { declaredUseType: { classPropertyName: "declaredUseType", publicName: "declaredUseType", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { enterLicense: "enterLicense" }, ngImport: i0, template: "@if (shouldShow()) {\n <div class=\"wm-license-nudge\">\n <span class=\"wm-license-nudge__text\">\n Using this at work? Organizations require a commercial license.\n </span>\n <div class=\"wm-license-nudge__actions\">\n <wm-button variant=\"ghost\" size=\"sm\" (click)=\"onEnterLicense()\">Enter License Key</wm-button>\n <wm-button variant=\"ghost\" size=\"sm\" (click)=\"dismiss()\">Dismiss</wm-button>\n </div>\n </div>\n}\n", styles: [".wm-license-nudge{display:flex;align-items:center;justify-content:space-between;gap:var(--wm-space-md, 12px);padding:var(--wm-space-sm, 8px) var(--wm-space-md, 12px);background:var(--wm-bg-subtle, #2a2a2a);border-bottom:1px solid var(--wm-border, #3a3a3a);font-size:var(--wm-font-sm, 13px);color:var(--wm-fg-secondary, #999)}.wm-license-nudge__text{flex:1}.wm-license-nudge__actions{display:flex;gap:var(--wm-space-xs, 4px);flex-shrink:0}\n"], dependencies: [{ kind: "component", type: WmButton, selector: "wm-button", inputs: ["variant", "size", "disabled", "type", "iconOnly", "ariaLabel", "btnTitle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
74
+ }
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseNudge, decorators: [{
76
+ type: Component,
77
+ args: [{ selector: 'wm-license-nudge', standalone: true, imports: [WmButton], changeDetection: ChangeDetectionStrategy.OnPush, template: "@if (shouldShow()) {\n <div class=\"wm-license-nudge\">\n <span class=\"wm-license-nudge__text\">\n Using this at work? Organizations require a commercial license.\n </span>\n <div class=\"wm-license-nudge__actions\">\n <wm-button variant=\"ghost\" size=\"sm\" (click)=\"onEnterLicense()\">Enter License Key</wm-button>\n <wm-button variant=\"ghost\" size=\"sm\" (click)=\"dismiss()\">Dismiss</wm-button>\n </div>\n </div>\n}\n", styles: [".wm-license-nudge{display:flex;align-items:center;justify-content:space-between;gap:var(--wm-space-md, 12px);padding:var(--wm-space-sm, 8px) var(--wm-space-md, 12px);background:var(--wm-bg-subtle, #2a2a2a);border-bottom:1px solid var(--wm-border, #3a3a3a);font-size:var(--wm-font-sm, 13px);color:var(--wm-fg-secondary, #999)}.wm-license-nudge__text{flex:1}.wm-license-nudge__actions{display:flex;gap:var(--wm-space-xs, 4px);flex-shrink:0}\n"] }]
78
+ }], propDecorators: { declaredUseType: [{ type: i0.Input, args: [{ isSignal: true, alias: "declaredUseType", required: false }] }], enterLicense: [{ type: i0.Output, args: ["enterLicense"] }] } });
79
+
80
+ class WmLicenseDialog {
81
+ licenseService = inject(WmLicenseService);
82
+ visible = input.required(...(ngDevMode ? [{ debugName: "visible" }] : /* istanbul ignore next */ []));
83
+ dismiss = output();
84
+ keyInput = signal('', ...(ngDevMode ? [{ debugName: "keyInput" }] : /* istanbul ignore next */ []));
85
+ validationResult = signal(null, ...(ngDevMode ? [{ debugName: "validationResult" }] : /* istanbul ignore next */ []));
86
+ validating = signal(false, ...(ngDevMode ? [{ debugName: "validating" }] : /* istanbul ignore next */ []));
87
+ installed = signal(false, ...(ngDevMode ? [{ debugName: "installed" }] : /* istanbul ignore next */ []));
88
+ onDismiss() {
89
+ this.reset();
90
+ this.dismiss.emit();
91
+ }
92
+ onKeyInput(event) {
93
+ const value = event.target.value;
94
+ this.keyInput.set(value);
95
+ this.validationResult.set(null);
96
+ this.installed.set(false);
97
+ }
98
+ async validate() {
99
+ const jwt = this.keyInput().trim();
100
+ if (!jwt)
101
+ return;
102
+ this.validating.set(true);
103
+ try {
104
+ const status = await this.licenseService.installKey(jwt);
105
+ this.validationResult.set(status);
106
+ if (status.valid) {
107
+ this.installed.set(true);
108
+ }
109
+ }
110
+ catch (e) {
111
+ this.validationResult.set({
112
+ valid: false,
113
+ org: null,
114
+ product: '',
115
+ seats: null,
116
+ tier: null,
117
+ expires: null,
118
+ key_id: null,
119
+ error: String(e),
120
+ });
121
+ }
122
+ finally {
123
+ this.validating.set(false);
124
+ }
125
+ }
126
+ reset() {
127
+ this.keyInput.set('');
128
+ this.validationResult.set(null);
129
+ this.validating.set(false);
130
+ this.installed.set(false);
131
+ }
132
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseDialog, deps: [], target: i0.ɵɵFactoryTarget.Component });
133
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.23", type: WmLicenseDialog, isStandalone: true, selector: "wm-license-dialog", inputs: { visible: { classPropertyName: "visible", publicName: "visible", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { dismiss: "dismiss" }, ngImport: i0, template: "<wm-modal [visible]=\"visible()\" (dismiss)=\"onDismiss()\">\n <div class=\"wm-license-dialog\">\n <h2 class=\"wm-license-dialog__title\">Enter License Key</h2>\n\n @if (installed()) {\n <div class=\"wm-license-dialog__success\">\n <p class=\"wm-license-dialog__success-text\">License activated successfully.</p>\n <dl class=\"wm-license-dialog__details\">\n <dt>Organization</dt>\n <dd>{{ validationResult()?.org }}</dd>\n <dt>Seats</dt>\n <dd>{{ validationResult()?.seats }}</dd>\n <dt>Expires</dt>\n <dd>{{ validationResult()?.expires?.slice(0, 10) }}</dd>\n </dl>\n <wm-button variant=\"primary\" size=\"sm\" (click)=\"onDismiss()\">Done</wm-button>\n </div>\n } @else {\n <textarea\n class=\"wm-license-dialog__input\"\n placeholder=\"Paste your license key here...\"\n [value]=\"keyInput()\"\n (input)=\"onKeyInput($event)\"\n rows=\"4\"\n ></textarea>\n\n @if (validationResult(); as result) {\n @if (!result.valid) {\n <p class=\"wm-license-dialog__error\">\n @if (result.error === 'expired') {\n This license key has expired.\n } @else {\n This is not a valid license key for this product.\n }\n </p>\n }\n }\n\n <div class=\"wm-license-dialog__actions\">\n <wm-button\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"!keyInput().trim() || validating()\"\n (click)=\"validate()\"\n >\n @if (validating()) { Validating... } @else { Activate }\n </wm-button>\n </div>\n }\n </div>\n</wm-modal>\n", styles: [".wm-license-dialog{background:var(--wm-bg-raised, #1e1e1e);border-radius:var(--wm-radius-md, 8px);padding:var(--wm-space-lg, 20px);width:480px;max-width:90vw;display:flex;flex-direction:column;gap:var(--wm-space-md, 12px)}.wm-license-dialog__title{font-size:var(--wm-font-lg, 16px);font-weight:600;color:var(--wm-fg-primary, #fff);margin:0}.wm-license-dialog__input{width:100%;resize:vertical;font-family:var(--wm-font-mono, monospace);font-size:var(--wm-font-sm, 13px);padding:var(--wm-space-sm, 8px);border:1px solid var(--wm-border, #3a3a3a);border-radius:var(--wm-radius-sm, 4px);background:var(--wm-bg-input, #141414);color:var(--wm-fg-primary, #fff);box-sizing:border-box}.wm-license-dialog__input:focus{outline:none;border-color:var(--wm-accent, #4a9eff)}.wm-license-dialog__error{color:var(--wm-fg-error, #f44);font-size:var(--wm-font-sm, 13px);margin:0}.wm-license-dialog__success{display:flex;flex-direction:column;gap:var(--wm-space-md, 12px)}.wm-license-dialog__success-text{color:var(--wm-fg-success, #4caf50);font-size:var(--wm-font-sm, 13px);margin:0}.wm-license-dialog__details{display:grid;grid-template-columns:auto 1fr;gap:var(--wm-space-xs, 4px) var(--wm-space-md, 12px);font-size:var(--wm-font-sm, 13px);margin:0}.wm-license-dialog__details dt{color:var(--wm-fg-secondary, #999)}.wm-license-dialog__details dd{color:var(--wm-fg-primary, #fff);margin:0}.wm-license-dialog__actions{display:flex;justify-content:flex-end;gap:var(--wm-space-sm, 8px)}\n"], dependencies: [{ kind: "component", type: WmModal, selector: "wm-modal", inputs: ["visible", "align"], outputs: ["dismiss"] }, { kind: "component", type: WmButton, selector: "wm-button", inputs: ["variant", "size", "disabled", "type", "iconOnly", "ariaLabel", "btnTitle"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
134
+ }
135
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.23", ngImport: i0, type: WmLicenseDialog, decorators: [{
136
+ type: Component,
137
+ args: [{ selector: 'wm-license-dialog', standalone: true, imports: [WmModal, WmButton], changeDetection: ChangeDetectionStrategy.OnPush, template: "<wm-modal [visible]=\"visible()\" (dismiss)=\"onDismiss()\">\n <div class=\"wm-license-dialog\">\n <h2 class=\"wm-license-dialog__title\">Enter License Key</h2>\n\n @if (installed()) {\n <div class=\"wm-license-dialog__success\">\n <p class=\"wm-license-dialog__success-text\">License activated successfully.</p>\n <dl class=\"wm-license-dialog__details\">\n <dt>Organization</dt>\n <dd>{{ validationResult()?.org }}</dd>\n <dt>Seats</dt>\n <dd>{{ validationResult()?.seats }}</dd>\n <dt>Expires</dt>\n <dd>{{ validationResult()?.expires?.slice(0, 10) }}</dd>\n </dl>\n <wm-button variant=\"primary\" size=\"sm\" (click)=\"onDismiss()\">Done</wm-button>\n </div>\n } @else {\n <textarea\n class=\"wm-license-dialog__input\"\n placeholder=\"Paste your license key here...\"\n [value]=\"keyInput()\"\n (input)=\"onKeyInput($event)\"\n rows=\"4\"\n ></textarea>\n\n @if (validationResult(); as result) {\n @if (!result.valid) {\n <p class=\"wm-license-dialog__error\">\n @if (result.error === 'expired') {\n This license key has expired.\n } @else {\n This is not a valid license key for this product.\n }\n </p>\n }\n }\n\n <div class=\"wm-license-dialog__actions\">\n <wm-button\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"!keyInput().trim() || validating()\"\n (click)=\"validate()\"\n >\n @if (validating()) { Validating... } @else { Activate }\n </wm-button>\n </div>\n }\n </div>\n</wm-modal>\n", styles: [".wm-license-dialog{background:var(--wm-bg-raised, #1e1e1e);border-radius:var(--wm-radius-md, 8px);padding:var(--wm-space-lg, 20px);width:480px;max-width:90vw;display:flex;flex-direction:column;gap:var(--wm-space-md, 12px)}.wm-license-dialog__title{font-size:var(--wm-font-lg, 16px);font-weight:600;color:var(--wm-fg-primary, #fff);margin:0}.wm-license-dialog__input{width:100%;resize:vertical;font-family:var(--wm-font-mono, monospace);font-size:var(--wm-font-sm, 13px);padding:var(--wm-space-sm, 8px);border:1px solid var(--wm-border, #3a3a3a);border-radius:var(--wm-radius-sm, 4px);background:var(--wm-bg-input, #141414);color:var(--wm-fg-primary, #fff);box-sizing:border-box}.wm-license-dialog__input:focus{outline:none;border-color:var(--wm-accent, #4a9eff)}.wm-license-dialog__error{color:var(--wm-fg-error, #f44);font-size:var(--wm-font-sm, 13px);margin:0}.wm-license-dialog__success{display:flex;flex-direction:column;gap:var(--wm-space-md, 12px)}.wm-license-dialog__success-text{color:var(--wm-fg-success, #4caf50);font-size:var(--wm-font-sm, 13px);margin:0}.wm-license-dialog__details{display:grid;grid-template-columns:auto 1fr;gap:var(--wm-space-xs, 4px) var(--wm-space-md, 12px);font-size:var(--wm-font-sm, 13px);margin:0}.wm-license-dialog__details dt{color:var(--wm-fg-secondary, #999)}.wm-license-dialog__details dd{color:var(--wm-fg-primary, #fff);margin:0}.wm-license-dialog__actions{display:flex;justify-content:flex-end;gap:var(--wm-space-sm, 8px)}\n"] }]
138
+ }], propDecorators: { visible: [{ type: i0.Input, args: [{ isSignal: true, alias: "visible", required: true }] }], dismiss: [{ type: i0.Output, args: ["dismiss"] }] } });
139
+
140
+ // @wildmason/aegis/license
141
+ //
142
+ // The license UI for Wildmason's Tauri apps. It is its own entry point because
143
+ // WmLicenseService calls Tauri commands: keeping it out of
144
+ // @wildmason/aegis/components means a web app never imports @tauri-apps/api.
145
+ // Shared components come from the main entry by package name, never by
146
+ // relative path, so they are not copied into this bundle.
147
+
148
+ /**
149
+ * Generated bundle index. Do not edit.
150
+ */
151
+
152
+ export { WmLicenseDialog, WmLicenseNudge, WmLicenseService };
153
+ //# sourceMappingURL=wildmason-aegis-license.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wildmason-aegis-license.mjs","sources":["../../license/license.service.ts","../../license/license-nudge.ts","../../license/license-nudge.html","../../license/license-dialog.ts","../../license/license-dialog.html","../../license/index.ts","../../license/wildmason-aegis-license.ts"],"sourcesContent":["import { Injectable, signal, computed, inject } from '@angular/core';\nimport { invoke } from '@tauri-apps/api/core';\nimport { WmToastService } from '@wildmason/aegis';\nimport type { LicenseStatus } from './license.types';\n\n@Injectable({ providedIn: 'root' })\nexport class WmLicenseService {\n private toast = inject(WmToastService);\n\n readonly license = signal<LicenseStatus | null>(null);\n readonly isLicensed = computed(() => this.license()?.valid ?? false);\n readonly orgName = computed(() => this.license()?.org ?? null);\n\n async checkLicense(): Promise<void> {\n try {\n const status = await invoke<LicenseStatus>('get_license_status');\n this.license.set(status);\n if (!status.valid && status.error) {\n const message = status.error === 'expired'\n ? 'Your license key has expired.'\n : 'Your license key is not valid.';\n this.toast.error(message);\n }\n } catch (e) {\n console.error('License check failed:', e);\n this.license.set(null);\n }\n }\n\n async installKey(jwt: string): Promise<LicenseStatus> {\n const status = await invoke<LicenseStatus>('install_license', { key: jwt });\n this.license.set(status);\n return status;\n }\n}\n","import { Component, ChangeDetectionStrategy, signal, output, input, inject } from '@angular/core';\nimport { WmLicenseService } from './license.service';\nimport { WmButton } from '@wildmason/aegis';\n\n/**\n * One of the values written to `AppConfig.declared_use_type` by the\n * first-run EULA modal in apps that gate on use-type self-attestation\n * (Mortar). Apps without a use-type system (Helm) leave this `null` and\n * the nudge falls back to its original behaviour: visible to anyone\n * without a valid commercial key.\n */\nexport type WmDeclaredUseType = 'personal' | 'oss' | 'commercial';\n\n@Component({\n selector: 'wm-license-nudge',\n standalone: true,\n imports: [WmButton],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './license-nudge.html',\n styleUrl: './license-nudge.css',\n})\nexport class WmLicenseNudge {\n private licenseService = inject(WmLicenseService);\n\n /**\n * Self-declared use type from the host app's first-run modal. When the\n * user has declared `personal` or `oss` the nudge stays hidden — they've\n * already told us a commercial key isn't relevant. `commercial` (without\n * a key) and `null` (no declaration yet) keep the nudge visible.\n *\n * Typed `string | null` rather than `WmDeclaredUseType | null` because\n * ng-packagr's partial-compilation d.ts emit drops the `null` from a\n * union with a string-literal type, which then forces every consumer to\n * `as` the binding. Runtime narrowing happens in `shouldShow`.\n */\n readonly declaredUseType = input<string | null>(null);\n\n dismissed = signal(false);\n enterLicense = output<void>();\n\n protected readonly shouldShow = () => {\n if (this.licenseService.isLicensed()) return false;\n if (this.dismissed()) return false;\n const ut = this.declaredUseType();\n if (ut === 'personal' || ut === 'oss') return false;\n return true;\n };\n\n dismiss(): void {\n this.dismissed.set(true);\n }\n\n onEnterLicense(): void {\n this.enterLicense.emit();\n }\n}\n","@if (shouldShow()) {\n <div class=\"wm-license-nudge\">\n <span class=\"wm-license-nudge__text\">\n Using this at work? Organizations require a commercial license.\n </span>\n <div class=\"wm-license-nudge__actions\">\n <wm-button variant=\"ghost\" size=\"sm\" (click)=\"onEnterLicense()\">Enter License Key</wm-button>\n <wm-button variant=\"ghost\" size=\"sm\" (click)=\"dismiss()\">Dismiss</wm-button>\n </div>\n </div>\n}\n","import { Component, ChangeDetectionStrategy, signal, input, output, inject } from '@angular/core';\nimport { WmButton, WmModal } from '@wildmason/aegis';\nimport { WmLicenseService } from './license.service';\nimport type { LicenseStatus } from './license.types';\n\n@Component({\n selector: 'wm-license-dialog',\n standalone: true,\n imports: [WmModal, WmButton],\n changeDetection: ChangeDetectionStrategy.OnPush,\n templateUrl: './license-dialog.html',\n styleUrl: './license-dialog.css',\n})\nexport class WmLicenseDialog {\n private licenseService = inject(WmLicenseService);\n\n visible = input.required<boolean>();\n dismiss = output<void>();\n\n keyInput = signal('');\n validationResult = signal<LicenseStatus | null>(null);\n validating = signal(false);\n installed = signal(false);\n\n onDismiss(): void {\n this.reset();\n this.dismiss.emit();\n }\n\n onKeyInput(event: Event): void {\n const value = (event.target as HTMLTextAreaElement).value;\n this.keyInput.set(value);\n this.validationResult.set(null);\n this.installed.set(false);\n }\n\n async validate(): Promise<void> {\n const jwt = this.keyInput().trim();\n if (!jwt) return;\n\n this.validating.set(true);\n try {\n const status = await this.licenseService.installKey(jwt);\n this.validationResult.set(status);\n if (status.valid) {\n this.installed.set(true);\n }\n } catch (e) {\n this.validationResult.set({\n valid: false,\n org: null,\n product: '',\n seats: null,\n tier: null,\n expires: null,\n key_id: null,\n error: String(e),\n });\n } finally {\n this.validating.set(false);\n }\n }\n\n private reset(): void {\n this.keyInput.set('');\n this.validationResult.set(null);\n this.validating.set(false);\n this.installed.set(false);\n }\n}\n","<wm-modal [visible]=\"visible()\" (dismiss)=\"onDismiss()\">\n <div class=\"wm-license-dialog\">\n <h2 class=\"wm-license-dialog__title\">Enter License Key</h2>\n\n @if (installed()) {\n <div class=\"wm-license-dialog__success\">\n <p class=\"wm-license-dialog__success-text\">License activated successfully.</p>\n <dl class=\"wm-license-dialog__details\">\n <dt>Organization</dt>\n <dd>{{ validationResult()?.org }}</dd>\n <dt>Seats</dt>\n <dd>{{ validationResult()?.seats }}</dd>\n <dt>Expires</dt>\n <dd>{{ validationResult()?.expires?.slice(0, 10) }}</dd>\n </dl>\n <wm-button variant=\"primary\" size=\"sm\" (click)=\"onDismiss()\">Done</wm-button>\n </div>\n } @else {\n <textarea\n class=\"wm-license-dialog__input\"\n placeholder=\"Paste your license key here...\"\n [value]=\"keyInput()\"\n (input)=\"onKeyInput($event)\"\n rows=\"4\"\n ></textarea>\n\n @if (validationResult(); as result) {\n @if (!result.valid) {\n <p class=\"wm-license-dialog__error\">\n @if (result.error === 'expired') {\n This license key has expired.\n } @else {\n This is not a valid license key for this product.\n }\n </p>\n }\n }\n\n <div class=\"wm-license-dialog__actions\">\n <wm-button\n variant=\"primary\"\n size=\"sm\"\n [disabled]=\"!keyInput().trim() || validating()\"\n (click)=\"validate()\"\n >\n @if (validating()) { Validating... } @else { Activate }\n </wm-button>\n </div>\n }\n </div>\n</wm-modal>\n","// @wildmason/aegis/license\n//\n// The license UI for Wildmason's Tauri apps. It is its own entry point because\n// WmLicenseService calls Tauri commands: keeping it out of\n// @wildmason/aegis/components means a web app never imports @tauri-apps/api.\n// Shared components come from the main entry by package name, never by\n// relative path, so they are not copied into this bundle.\n\nexport { WmLicenseService } from './license.service';\nexport { WmLicenseNudge } from './license-nudge';\nexport type { WmDeclaredUseType } from './license-nudge';\nexport { WmLicenseDialog } from './license-dialog';\nexport type { LicenseStatus } from './license.types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}