@franzzemen/brokenstock-shell 0.6.1 → 0.7.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.
|
@@ -32,6 +32,23 @@ import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
|
32
32
|
import * as i1$2 from '@angular/material/dialog';
|
|
33
33
|
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Does `url` fall inside any of `patterns`? Query string and fragment are
|
|
37
|
+
* stripped; a pattern matches when the path equals it or continues past it at
|
|
38
|
+
* a `/`; a `:param` segment matches any one non-empty segment. Same rule as
|
|
39
|
+
* the lexicon's `matchesRoute`, tested there and here.
|
|
40
|
+
*/
|
|
41
|
+
function matchesRoute(url, patterns) {
|
|
42
|
+
const path = url.split('?')[0].split('#')[0].replace(/\/+$/, '') || '/';
|
|
43
|
+
const segments = path.split('/');
|
|
44
|
+
return patterns.some(pattern => {
|
|
45
|
+
const want = pattern.replace(/\/+$/, '').split('/');
|
|
46
|
+
if (want.length > segments.length)
|
|
47
|
+
return false;
|
|
48
|
+
return want.every((w, i) => (w.startsWith(':') ? segments[i].length > 0 : w === segments[i]));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
35
52
|
const SHELL_APP_NAME = new InjectionToken('SHELL_APP_NAME');
|
|
36
53
|
const SHELL_MENU_ITEMS = new InjectionToken('SHELL_MENU_ITEMS');
|
|
37
54
|
const SHELL_AUTH_URLS = new InjectionToken('SHELL_AUTH_URLS');
|
|
@@ -2557,6 +2574,7 @@ class FaqCommand {
|
|
|
2557
2574
|
this.description = 'Open the in-app FAQ help system';
|
|
2558
2575
|
this.usage = FAQ_USAGE;
|
|
2559
2576
|
this.requiresAuth = false;
|
|
2577
|
+
this.scope = 'global';
|
|
2560
2578
|
this.argsSchema = z.array(z.string()).max(50);
|
|
2561
2579
|
}
|
|
2562
2580
|
execute(args) {
|
|
@@ -2716,6 +2734,7 @@ class CommandService {
|
|
|
2716
2734
|
...(ngDevMode ? [{ debugName: "helpOverlay" }] : /* istanbul ignore next */ []));
|
|
2717
2735
|
this.authService = inject(AuthenticationService);
|
|
2718
2736
|
this.registry = inject(CommandRegistryService);
|
|
2737
|
+
this.router = inject(Router);
|
|
2719
2738
|
this.lastResult = computed(() => {
|
|
2720
2739
|
const h = this.history();
|
|
2721
2740
|
return h.length > 0 ? h[h.length - 1].result : null;
|
|
@@ -2845,9 +2864,12 @@ class CommandService {
|
|
|
2845
2864
|
' help — List available commands',
|
|
2846
2865
|
// Unavailable commands stay listed, annotated. Removing them would make
|
|
2847
2866
|
// the CLI look broken rather than gated.
|
|
2867
|
+
// A command scoped to another screen stays listed too, with its screen
|
|
2868
|
+
// named rather than hidden (trades-tree.prd.md D31): hiding it would
|
|
2869
|
+
// read as a broken CLI.
|
|
2848
2870
|
...registered.map(c => {
|
|
2849
2871
|
const availability = c.isAvailable?.();
|
|
2850
|
-
const suffix = availability && !availability.ok ? ` [unavailable — ${availability.reason}]` :
|
|
2872
|
+
const suffix = availability && !availability.ok ? ` [unavailable — ${availability.reason}]` : this.#scopeSuffix(c.scope);
|
|
2851
2873
|
return ` ${c.name} — ${c.description}${suffix}`;
|
|
2852
2874
|
}),
|
|
2853
2875
|
];
|
|
@@ -2896,6 +2918,19 @@ class CommandService {
|
|
|
2896
2918
|
if (hasHelp) {
|
|
2897
2919
|
return { success: true, message: def.usage, timestamp: new Date(), isHelp: true };
|
|
2898
2920
|
}
|
|
2921
|
+
// Screen gate (trades-tree.prd.md D30). A command bound to state only one
|
|
2922
|
+
// screen sets is refused off that screen with the screen named, the way
|
|
2923
|
+
// `tab` always did on its own. A command that navigates to its screen is
|
|
2924
|
+
// never refused here — declaring its routes is what lets `help` say where
|
|
2925
|
+
// it lives, and taking the user there is the handler's job (D32).
|
|
2926
|
+
if (def.scope !== 'global' && def.scope.elsewhere === 'refuse' && !matchesRoute(this.router.url, def.scope.routes)) {
|
|
2927
|
+
return {
|
|
2928
|
+
success: false,
|
|
2929
|
+
message: `'${command}' works on ${def.scope.screen}.`,
|
|
2930
|
+
severity: 'error',
|
|
2931
|
+
timestamp: new Date(),
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2899
2934
|
// Readiness gate. Deliberately ahead of argument validation: "you can't use
|
|
2900
2935
|
// this yet, here's why" is more useful than a complaint about the arguments
|
|
2901
2936
|
// of a command that wouldn't have run anyway.
|
|
@@ -2926,7 +2961,14 @@ class CommandService {
|
|
|
2926
2961
|
// user decides whether it's worth unlocking. The gate is noted, not enforced.
|
|
2927
2962
|
const availability = def.isAvailable?.();
|
|
2928
2963
|
const note = availability && !availability.ok ? `\n\nCurrently unavailable — ${availability.reason}` : '';
|
|
2929
|
-
|
|
2964
|
+
const where = def.scope === 'global' ? '' : `\n\nWorks on ${def.scope.screen}.`;
|
|
2965
|
+
return { success: true, message: def.usage + where + note, timestamp: new Date(), isHelp: true };
|
|
2966
|
+
}
|
|
2967
|
+
/** `help`'s annotation for a command scoped to a screen other than the current one. */
|
|
2968
|
+
#scopeSuffix(scope) {
|
|
2969
|
+
if (scope === 'global' || matchesRoute(this.router.url, scope.routes))
|
|
2970
|
+
return '';
|
|
2971
|
+
return ` [${scope.screen}]`;
|
|
2930
2972
|
}
|
|
2931
2973
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
2932
2974
|
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CommandService, providedIn: 'root' }); }
|
|
@@ -3463,5 +3505,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
3463
3505
|
* Generated bundle index. Do not edit.
|
|
3464
3506
|
*/
|
|
3465
3507
|
|
|
3466
|
-
export { AuthenticationService, CLEAR_USAGE, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, buildFallbackChain, decideRenewal, guestGuard, provideShellConfig, tokenize };
|
|
3508
|
+
export { AuthenticationService, CLEAR_USAGE, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FAQ_PUBLIC_PUBLISHER, FAQ_PUBLIC_SITE_BASE_URL, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, buildFallbackChain, decideRenewal, guestGuard, matchesRoute, provideShellConfig, tokenize };
|
|
3467
3509
|
//# sourceMappingURL=franzzemen-brokenstock-shell.mjs.map
|