@franzzemen/brokenstock-shell 0.0.28 → 0.0.29

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,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, makeEnvironmentProviders, signal, computed, Injectable, inject, input, Directive, isDevMode, effect, untracked, HostListener, ViewChild, ChangeDetectionStrategy, Component, NgZone, viewChild, afterNextRender } from '@angular/core';
3
3
  import * as i2$1 from '@angular/router';
4
- import { Router, RouterLink, RouterLinkActive, RouterOutlet, ActivatedRoute } from '@angular/router';
4
+ import { Router, RouterLink, ActivatedRoute, RouterLinkActive, RouterOutlet } from '@angular/router';
5
5
  import { HttpHeader, BEARER_TOKEN_REGEX, USERNAME_REGEX, PASSWORD_REGEX } from '@franzzemen/utility';
6
6
  import { validateCredentials } from '@franzzemen/identity';
7
7
  import { HttpClient } from '@angular/common/http';
@@ -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, DOCUMENT, DatePipe } from '@angular/common';
14
+ import { NgClass, CommonModule, DOCUMENT, DatePipe } from '@angular/common';
15
15
  import { z } from 'zod/v4';
16
16
  import * as i1 from '@angular/material/icon';
17
17
  import { MatIconModule } from '@angular/material/icon';
@@ -1236,6 +1236,211 @@ var faqAuthoringPanel = /*#__PURE__*/Object.freeze({
1236
1236
  FaqAuthoringPanel: FaqAuthoringPanel
1237
1237
  });
1238
1238
 
1239
+ /**
1240
+ * Public, SEO-targeted FAQ index. Prerendered at build time on the
1241
+ * consumer side (e.g. /help in broken-stock). Lists every manifest
1242
+ * entry where `audience === 'public' && seoReady === true`, grouped
1243
+ * by category and ordered by `order`.
1244
+ */
1245
+ class PublicFaqIndex {
1246
+ constructor() {
1247
+ this.loader = inject(FAQ_CONTENT_LOADER);
1248
+ this._entries = signal([], ...(ngDevMode ? [{ debugName: "_entries" }] : /* istanbul ignore next */ []));
1249
+ this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
1250
+ this.groups = computed(() => {
1251
+ const visible = this._entries().filter((e) => e.audience === 'public' && e.seoReady);
1252
+ const byCategory = new Map();
1253
+ for (const entry of visible) {
1254
+ const list = byCategory.get(entry.category) ?? [];
1255
+ list.push(entry);
1256
+ byCategory.set(entry.category, list);
1257
+ }
1258
+ const groups = [];
1259
+ for (const [category, entries] of byCategory) {
1260
+ entries.sort((a, b) => a.order - b.order || a.title.localeCompare(b.title));
1261
+ groups.push({ category, entries });
1262
+ }
1263
+ groups.sort((a, b) => a.category.localeCompare(b.category));
1264
+ return groups;
1265
+ }, ...(ngDevMode ? [{ debugName: "groups" }] : /* istanbul ignore next */ []));
1266
+ }
1267
+ async ngOnInit() {
1268
+ try {
1269
+ const manifest = await this.loader.loadManifest();
1270
+ this._entries.set(manifest.entries);
1271
+ }
1272
+ finally {
1273
+ this.loading.set(false);
1274
+ }
1275
+ }
1276
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PublicFaqIndex, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1277
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: PublicFaqIndex, isStandalone: true, selector: "public-faq-index", ngImport: i0, template: `
1278
+ <article class="public-faq-index">
1279
+ <header>
1280
+ <h1>Brokenstock Help</h1>
1281
+ <p class="lead">Documentation and walkthroughs for the Brokenstock trade-journal platform.</p>
1282
+ </header>
1283
+ @if (loading()) {
1284
+ <p>Loading…</p>
1285
+ } @else if (groups().length === 0) {
1286
+ <p>No help pages are published yet.</p>
1287
+ } @else {
1288
+ @for (group of groups(); track group.category) {
1289
+ <section>
1290
+ <h2>{{ group.category }}</h2>
1291
+ <ul>
1292
+ @for (entry of group.entries; track entry.key) {
1293
+ <li>
1294
+ <a [routerLink]="['/help', entry.file]">{{ entry.title }}</a>
1295
+ @if (entry.description) {
1296
+ <span class="desc"> — {{ entry.description }}</span>
1297
+ }
1298
+ </li>
1299
+ }
1300
+ </ul>
1301
+ </section>
1302
+ }
1303
+ }
1304
+ </article>
1305
+ `, isInline: true, styles: [":host{display:block}.public-faq-index{max-width:860px;margin:0 auto;padding:2rem 1.25rem 4rem;font-family:system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.55}header h1{font-size:2.25rem;margin:0 0 .5rem}.lead{color:#555;margin:0 0 2rem;font-size:1.1rem}h2{font-size:1.35rem;margin:1.75rem 0 .5rem;border-bottom:1px solid #eee;padding-bottom:.25rem}ul{list-style:none;padding:0;margin:0}li{padding:.4rem 0}a{color:#1565c0;text-decoration:none;font-weight:500}a:hover{text-decoration:underline}.desc{color:#666;font-weight:400}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
1306
+ }
1307
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PublicFaqIndex, decorators: [{
1308
+ type: Component,
1309
+ args: [{ selector: 'public-faq-index', standalone: true, imports: [CommonModule, RouterLink], template: `
1310
+ <article class="public-faq-index">
1311
+ <header>
1312
+ <h1>Brokenstock Help</h1>
1313
+ <p class="lead">Documentation and walkthroughs for the Brokenstock trade-journal platform.</p>
1314
+ </header>
1315
+ @if (loading()) {
1316
+ <p>Loading…</p>
1317
+ } @else if (groups().length === 0) {
1318
+ <p>No help pages are published yet.</p>
1319
+ } @else {
1320
+ @for (group of groups(); track group.category) {
1321
+ <section>
1322
+ <h2>{{ group.category }}</h2>
1323
+ <ul>
1324
+ @for (entry of group.entries; track entry.key) {
1325
+ <li>
1326
+ <a [routerLink]="['/help', entry.file]">{{ entry.title }}</a>
1327
+ @if (entry.description) {
1328
+ <span class="desc"> — {{ entry.description }}</span>
1329
+ }
1330
+ </li>
1331
+ }
1332
+ </ul>
1333
+ </section>
1334
+ }
1335
+ }
1336
+ </article>
1337
+ `, styles: [":host{display:block}.public-faq-index{max-width:860px;margin:0 auto;padding:2rem 1.25rem 4rem;font-family:system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.55}header h1{font-size:2.25rem;margin:0 0 .5rem}.lead{color:#555;margin:0 0 2rem;font-size:1.1rem}h2{font-size:1.35rem;margin:1.75rem 0 .5rem;border-bottom:1px solid #eee;padding-bottom:.25rem}ul{list-style:none;padding:0;margin:0}li{padding:.4rem 0}a{color:#1565c0;text-decoration:none;font-weight:500}a:hover{text-decoration:underline}.desc{color:#666;font-weight:400}\n"] }]
1338
+ }] });
1339
+
1340
+ /**
1341
+ * Public, SEO-targeted FAQ page. Prerendered at build time per
1342
+ * qualifying manifest entry (e.g. /help/landing, /help/trade/segments).
1343
+ *
1344
+ * The route is `help/**` so multi-segment file paths work; this
1345
+ * component reads the wildcard path off `ActivatedRoute.url`.
1346
+ */
1347
+ class PublicFaqPage {
1348
+ constructor() {
1349
+ this.route = inject(ActivatedRoute);
1350
+ this.loader = inject(FAQ_CONTENT_LOADER);
1351
+ this.renderer = inject(FaqMarkdownRendererService);
1352
+ this.entry = signal(null, ...(ngDevMode ? [{ debugName: "entry" }] : /* istanbul ignore next */ []));
1353
+ this.body = signal('', ...(ngDevMode ? [{ debugName: "body" }] : /* istanbul ignore next */ []));
1354
+ this.loading = signal(true, ...(ngDevMode ? [{ debugName: "loading" }] : /* istanbul ignore next */ []));
1355
+ this.renderedHtml = computed(() => {
1356
+ const entry = this.entry();
1357
+ const body = this.body();
1358
+ if (!entry || !body)
1359
+ return '';
1360
+ return this.renderer.render(body, {
1361
+ context: 'page',
1362
+ imagePrefix: entry.imagePrefix
1363
+ ? `/assets/faq/${entry.imagePrefix}`
1364
+ : undefined,
1365
+ });
1366
+ }, ...(ngDevMode ? [{ debugName: "renderedHtml" }] : /* istanbul ignore next */ []));
1367
+ }
1368
+ async ngOnInit() {
1369
+ try {
1370
+ const segments = this.route.snapshot.url.map((s) => s.path);
1371
+ const filePath = segments.join('/');
1372
+ if (!filePath) {
1373
+ return;
1374
+ }
1375
+ const manifest = await this.loader.loadManifest();
1376
+ const match = manifest.entries.find((e) => e.file === filePath &&
1377
+ e.audience === 'public' &&
1378
+ e.seoReady);
1379
+ if (!match)
1380
+ return;
1381
+ const content = await this.loader.loadContent(match.key);
1382
+ if (content === null)
1383
+ return;
1384
+ const parsed = this.renderer.parse(content);
1385
+ this.entry.set(match);
1386
+ this.body.set(parsed.body);
1387
+ }
1388
+ finally {
1389
+ this.loading.set(false);
1390
+ }
1391
+ }
1392
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PublicFaqPage, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
1393
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.13", type: PublicFaqPage, isStandalone: true, selector: "public-faq-page", ngImport: i0, template: `
1394
+ <article class="public-faq-page">
1395
+ <nav class="crumbs">
1396
+ <a routerLink="/help">← Help</a>
1397
+ </nav>
1398
+ @if (loading()) {
1399
+ <p>Loading…</p>
1400
+ } @else if (!entry()) {
1401
+ <header>
1402
+ <h1>Page not found</h1>
1403
+ <p>That help page doesn't exist or isn't published. Try the <a routerLink="/help">help index</a>.</p>
1404
+ </header>
1405
+ } @else {
1406
+ <header>
1407
+ <h1>{{ entry()!.title }}</h1>
1408
+ @if (entry()!.description) {
1409
+ <p class="lead">{{ entry()!.description }}</p>
1410
+ }
1411
+ </header>
1412
+ <div class="body" [innerHTML]="renderedHtml()"></div>
1413
+ }
1414
+ </article>
1415
+ `, isInline: true, styles: [":host{display:block}.public-faq-page{max-width:860px;margin:0 auto;padding:2rem 1.25rem 4rem;font-family:system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.6}.crumbs{margin-bottom:1.5rem;font-size:.9rem}.crumbs a{color:#1565c0;text-decoration:none}.crumbs a:hover{text-decoration:underline}header h1{font-size:2.1rem;margin:0 0 .5rem}.lead{color:#555;font-size:1.1rem;margin:0 0 1.5rem}.body :is(h1,h2,h3,h4){margin-top:2rem}.body a{color:#1565c0}.body img{max-width:100%;height:auto}.body pre{background:#f6f8fa;padding:.75rem 1rem;border-radius:6px;overflow-x:auto}.body code{background:#f6f8fa;padding:.1rem .3rem;border-radius:3px;font-size:.9em}.body pre code{background:transparent;padding:0}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "routerLink"] }] }); }
1416
+ }
1417
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImport: i0, type: PublicFaqPage, decorators: [{
1418
+ type: Component,
1419
+ args: [{ selector: 'public-faq-page', standalone: true, imports: [CommonModule, RouterLink], template: `
1420
+ <article class="public-faq-page">
1421
+ <nav class="crumbs">
1422
+ <a routerLink="/help">← Help</a>
1423
+ </nav>
1424
+ @if (loading()) {
1425
+ <p>Loading…</p>
1426
+ } @else if (!entry()) {
1427
+ <header>
1428
+ <h1>Page not found</h1>
1429
+ <p>That help page doesn't exist or isn't published. Try the <a routerLink="/help">help index</a>.</p>
1430
+ </header>
1431
+ } @else {
1432
+ <header>
1433
+ <h1>{{ entry()!.title }}</h1>
1434
+ @if (entry()!.description) {
1435
+ <p class="lead">{{ entry()!.description }}</p>
1436
+ }
1437
+ </header>
1438
+ <div class="body" [innerHTML]="renderedHtml()"></div>
1439
+ }
1440
+ </article>
1441
+ `, styles: [":host{display:block}.public-faq-page{max-width:860px;margin:0 auto;padding:2rem 1.25rem 4rem;font-family:system-ui,-apple-system,Segoe UI,sans-serif;line-height:1.6}.crumbs{margin-bottom:1.5rem;font-size:.9rem}.crumbs a{color:#1565c0;text-decoration:none}.crumbs a:hover{text-decoration:underline}header h1{font-size:2.1rem;margin:0 0 .5rem}.lead{color:#555;font-size:1.1rem;margin:0 0 1.5rem}.body :is(h1,h2,h3,h4){margin-top:2rem}.body a{color:#1565c0}.body img{max-width:100%;height:auto}.body pre{background:#f6f8fa;padding:.75rem 1rem;border-radius:6px;overflow-x:auto}.body code{background:#f6f8fa;padding:.1rem .3rem;border-radius:3px;font-size:.9em}.body pre code{background:transparent;padding:0}\n"] }]
1442
+ }] });
1443
+
1239
1444
  class ThemeService {
1240
1445
  constructor() {
1241
1446
  this.mode = signal('dark', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
@@ -2048,5 +2253,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.13", ngImpo
2048
2253
  * Generated bundle index. Do not edit.
2049
2254
  */
2050
2255
 
2051
- export { AuthenticationService, CLEAR_USAGE, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, RegexValidatorDirective, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_POST_LOGIN_ROUTE, Shell, SideMenu, StatusBar, TerminalFocusService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, buildFallbackChain, guestGuard, provideShellConfig, tokenize };
2256
+ export { AuthenticationService, CLEAR_USAGE, CommandRegistryService, CommandService, CommandTerminal, ConfirmDialog, FAQ_CONTENT_LOADER, FAQ_DEV_EDITOR, FAQ_DEV_MODE, FAQ_DEV_WORKSPACE_ROOT, FaqAuthoringPanel, FaqHistoryService, FaqMarkdownRendererService, FaqModal, FaqService, HELP_USAGE, HttpFaqContentLoader, KeyboardShortcutService, Login, MainContent, PublicFaqIndex, PublicFaqPage, RegexValidatorDirective, SHELL_APP_NAME, SHELL_AUTH_URLS, SHELL_COMMANDS, SHELL_MENU_ITEMS, SHELL_POST_LOGIN_ROUTE, Shell, SideMenu, StatusBar, TerminalFocusService, ThemeService, ThinkingService, audienceAllowed, authGuard, authInterceptor, buildFallbackChain, guestGuard, provideShellConfig, tokenize };
2052
2257
  //# sourceMappingURL=franzzemen-brokenstock-shell.mjs.map