@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 +113 -2
- package/dist/fesm2022/wildmason-aegis-license.mjs +153 -0
- package/dist/fesm2022/wildmason-aegis-license.mjs.map +1 -0
- package/dist/fesm2022/wildmason-aegis.mjs +626 -294
- package/dist/fesm2022/wildmason-aegis.mjs.map +1 -1
- package/dist/types/wildmason-aegis-license.d.ts +73 -0
- package/dist/types/wildmason-aegis.d.ts +110 -71
- package/fonts/CascadiaCode-OFL.txt +94 -94
- package/inputs.css +43 -26
- package/package.json +27 -13
package/CHANGELOG.md
CHANGED
|
@@ -18,7 +18,116 @@ that 404s.
|
|
|
18
18
|
|
|
19
19
|
## [Unreleased]
|
|
20
20
|
|
|
21
|
-
|
|
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.
|
|
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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"}
|