@franzzemen/brokenstock-shell 0.9.2 → 0.9.3

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.
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, makeEnvironmentProviders, signal, computed, Injectable, inject, input, Directive, isDevMode, effect, untracked, HostListener, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, DestroyRef, NgZone, viewChild, afterNextRender, output } from '@angular/core';
2
+ import { InjectionToken, makeEnvironmentProviders, signal, computed, Injectable, inject, input, Directive, isDevMode, effect, untracked, HostListener, ViewChild, ViewEncapsulation, ChangeDetectionStrategy, Component, DestroyRef, NgZone, PLATFORM_ID, viewChild, afterNextRender, output } from '@angular/core';
3
3
  import * as i2$1 from '@angular/router';
4
4
  import { Router, RouterLink, RouterLinkActive, ActivatedRoute, RouterOutlet } from '@angular/router';
5
5
  import { BEARER_TOKEN_REGEX, HttpHeader, USERNAME_REGEX, PASSWORD_REGEX } from '@franzzemen/utility';
@@ -11,7 +11,7 @@ import { NG_VALIDATORS, FormsModule } from '@angular/forms';
11
11
  import { Marked } from 'marked';
12
12
  import { gfmHeadingId } from 'marked-gfm-heading-id';
13
13
  import fm from 'front-matter';
14
- import { NgClass, CommonModule, DOCUMENT, DatePipe } from '@angular/common';
14
+ import { NgClass, CommonModule, DOCUMENT, isPlatformBrowser, DatePipe } from '@angular/common';
15
15
  import { Title, Meta } from '@angular/platform-browser';
16
16
  import { z } from 'zod/v4';
17
17
  import * as i1 from '@angular/material/icon';
@@ -2844,6 +2844,98 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
2844
2844
  args: [{ providedIn: 'root' }]
2845
2845
  }], ctorParameters: () => [] });
2846
2846
 
2847
+ /*
2848
+ Created by Franz Zemen 2026-10-18
2849
+ License Type: UNLICENSED
2850
+
2851
+ Keys a screen owns while it is open: trading hotkeys (live-trading-foundation.prd.md D24, E8;
2852
+ live-trading.prd.md D34).
2853
+
2854
+ `KeyboardShortcutService` cannot do this job, and is not changed to: it allows only Alt and Shift
2855
+ and switches itself off inside any input. Hotkeys need the opposite on both counts:
2856
+
2857
+ - **Ctrl is allowed** (the defaults are Shift+F1 and Ctrl+F1). Meta (the Windows/Command key) never
2858
+ matches.
2859
+ - **They work while the cursor is in the command terminal**, the one input that is not a place to
2860
+ type a number. They do NOT fire while focus is in any other input, textarea, select or editable
2861
+ element: a key there is typing.
2862
+ - **Matched on `event.code`**, the physical key, never the character produced.
2863
+ - **Held-down repeats are ignored** (`event.repeat`): one order per press.
2864
+ - **Only on the routes a registration names.**
2865
+
2866
+ When a handler takes a key, the browser's own action for it is prevented and it goes no further.
2867
+ */
2868
+ /** The class the command terminal's input carries; keys typed there still reach a screen's keys. */
2869
+ const TERMINAL_INPUT_CLASS = 'terminal-input';
2870
+ /** True when the key is typed into a box other than the command terminal. */
2871
+ function isTypingElsewhere(target) {
2872
+ const el = target;
2873
+ if (!el || typeof el.tagName !== 'string')
2874
+ return false;
2875
+ if (el.classList?.contains(TERMINAL_INPUT_CLASS))
2876
+ return false;
2877
+ if (el.isContentEditable)
2878
+ return true;
2879
+ const tag = el.tagName.toUpperCase();
2880
+ if (tag === 'TEXTAREA' || tag === 'SELECT')
2881
+ return true;
2882
+ if (tag !== 'INPUT')
2883
+ return false;
2884
+ const type = (el.type || 'text').toLowerCase();
2885
+ return !['checkbox', 'radio', 'button', 'submit', 'reset', 'range', 'color', 'file', 'image'].includes(type);
2886
+ }
2887
+ /** Exactly this combination: the key, and these modifiers and no others. */
2888
+ function comboMatches(combo, e) {
2889
+ return e.code === combo.code && e.ctrlKey === combo.ctrl && e.shiftKey === combo.shift && e.altKey === combo.alt && !e.metaKey;
2890
+ }
2891
+ /** Whether a keydown may reach a screen's keys at all. */
2892
+ function isEligibleKey(e) {
2893
+ return !e.repeat && !e.isComposing && !isTypingElsewhere(e.target);
2894
+ }
2895
+ class ScreenKeysService {
2896
+ #document = inject(DOCUMENT);
2897
+ #zone = inject(NgZone);
2898
+ #router = inject(Router);
2899
+ #registrations = new Set();
2900
+ #listening = false;
2901
+ constructor() {
2902
+ if (!isPlatformBrowser(inject(PLATFORM_ID)))
2903
+ return;
2904
+ inject(DestroyRef).onDestroy(() => this.#document.removeEventListener('keydown', this.#onKey, true));
2905
+ }
2906
+ /** Registers keys for a screen; returns the function that removes them. */
2907
+ register(registration) {
2908
+ this.#registrations.add(registration);
2909
+ if (!this.#listening) {
2910
+ this.#listening = true;
2911
+ // Capture phase, so a screen's key is seen before a focused control can act on it.
2912
+ this.#zone.runOutsideAngular(() => this.#document.addEventListener('keydown', this.#onKey, true));
2913
+ }
2914
+ return () => this.#registrations.delete(registration);
2915
+ }
2916
+ #onKey = (e) => {
2917
+ if (this.#registrations.size === 0 || !isEligibleKey(e))
2918
+ return;
2919
+ const url = this.#router.url;
2920
+ for (const r of this.#registrations) {
2921
+ if (!matchesRoute(url, r.routes))
2922
+ continue;
2923
+ const taken = this.#zone.run(() => r.handle(e));
2924
+ if (taken) {
2925
+ e.preventDefault();
2926
+ e.stopPropagation();
2927
+ return;
2928
+ }
2929
+ }
2930
+ };
2931
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ScreenKeysService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2932
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ScreenKeysService, providedIn: 'root' }); }
2933
+ }
2934
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: ScreenKeysService, decorators: [{
2935
+ type: Injectable,
2936
+ args: [{ providedIn: 'root' }]
2937
+ }], ctorParameters: () => [] });
2938
+
2847
2939
  const FAQ_USAGE = 'faq\n' +
2848
2940
  'faq <key>\n' +
2849
2941
  'faq search <term>\n' +
@@ -4490,5 +4582,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
4490
4582
  * Generated bundle index. Do not edit.
4491
4583
  */
4492
4584
 
4493
- export { AuthenticationService, AuthenticatorError, AuthenticatorSettings, CLEAR_USAGE, CODE_STEP, CodeDialog, 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, PendingPromptState, PhoneLayoutService, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RecoveryCodes, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_PHONE_MEDIA_QUERY, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, SessionTokenStore, Shell, SideMenu, StatusBar, TOO_MANY_ATTEMPTS, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, answerEcho, audienceAllowed, authGuard, authInterceptor, authenticatorMessage, buildFallbackChain, buildLoginBody, decideRenewal, guestGuard, isYes, matchesRoute, openCodeDialog, provideShellConfig, recoveryCodesFile, tokenize };
4585
+ export { AuthenticationService, AuthenticatorError, AuthenticatorSettings, CLEAR_USAGE, CODE_STEP, CodeDialog, 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, PendingPromptState, PhoneLayoutService, PublicFaqIndex, PublicFaqPage, PublicFaqToc, RecoveryCodes, RegexValidatorDirective, SESSION_TOKEN_KEY, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_PHONE_MEDIA_QUERY, SHELL_POST_LOGIN_ROUTE, SHELL_SHARED_SESSION, ScreenKeysService, SessionTokenStore, Shell, SideMenu, StatusBar, TERMINAL_INPUT_CLASS, TOO_MANY_ATTEMPTS, TerminalFocusService, TerminalVisibilityService, ThemeService, ThinkingService, answerEcho, audienceAllowed, authGuard, authInterceptor, authenticatorMessage, buildFallbackChain, buildLoginBody, comboMatches, decideRenewal, guestGuard, isEligibleKey, isTypingElsewhere, isYes, matchesRoute, openCodeDialog, provideShellConfig, recoveryCodesFile, tokenize };
4494
4586
  //# sourceMappingURL=franzzemen-brokenstock-shell.mjs.map