valtech-components 2.0.729 → 2.0.731

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.
Files changed (32) hide show
  1. package/esm2022/lib/components/organisms/bottom-nav/bottom-nav.component.mjs +4 -4
  2. package/esm2022/lib/components/organisms/bottom-nav/types.mjs +2 -2
  3. package/esm2022/lib/config/company-footer.config.mjs +34 -20
  4. package/esm2022/lib/services/legal-link/legal-link.service.mjs +68 -0
  5. package/esm2022/lib/services/markdown-article/legal-content.service.mjs +65 -14
  6. package/esm2022/lib/services/markdown-article/markdown-article-parser.mjs +266 -0
  7. package/esm2022/lib/services/markdown-article/markdown-article-parser.service.mjs +6 -275
  8. package/esm2022/lib/services/preferences/index.mjs +3 -0
  9. package/esm2022/lib/services/preferences/preferences.service.mjs +164 -0
  10. package/esm2022/lib/services/preferences/preferences.types.mjs +7 -0
  11. package/esm2022/lib/version.mjs +2 -2
  12. package/esm2022/public-api.mjs +10 -1
  13. package/fesm2022/valtech-components.mjs +555 -267
  14. package/fesm2022/valtech-components.mjs.map +1 -1
  15. package/lib/components/atoms/rights-footer/rights-footer.component.d.ts +1 -1
  16. package/lib/components/atoms/text/text.component.d.ts +1 -1
  17. package/lib/components/molecules/features-list/features-list.component.d.ts +1 -1
  18. package/lib/components/organisms/article/article.component.d.ts +3 -3
  19. package/lib/components/organisms/bottom-nav/bottom-nav.component.d.ts +1 -1
  20. package/lib/components/organisms/bottom-nav/types.d.ts +1 -1
  21. package/lib/components/organisms/toolbar/toolbar.component.d.ts +1 -1
  22. package/lib/config/company-footer.config.d.ts +24 -4
  23. package/lib/services/legal-link/legal-link.service.d.ts +59 -0
  24. package/lib/services/markdown-article/legal-content.service.d.ts +49 -7
  25. package/lib/services/markdown-article/markdown-article-parser.d.ts +16 -0
  26. package/lib/services/markdown-article/markdown-article-parser.service.d.ts +3 -22
  27. package/lib/services/preferences/index.d.ts +2 -0
  28. package/lib/services/preferences/preferences.service.d.ts +51 -0
  29. package/lib/services/preferences/preferences.types.d.ts +35 -0
  30. package/lib/version.d.ts +1 -1
  31. package/package.json +1 -1
  32. package/public-api.d.ts +3 -0
@@ -50,7 +50,7 @@ import 'prismjs/components/prism-json';
50
50
  * Current version of valtech-components.
51
51
  * This is automatically updated during the publish process.
52
52
  */
53
- const VERSION = '2.0.729';
53
+ const VERSION = '2.0.731';
54
54
 
55
55
  /**
56
56
  * Servicio para gestionar presets de componentes.
@@ -32472,7 +32472,7 @@ const BOTTOM_NAV_DEFAULTS = {
32472
32472
  hideLabels: false,
32473
32473
  safeArea: true,
32474
32474
  animation: 'scale',
32475
- maxWidth: 'xl',
32475
+ maxWidth: 'md',
32476
32476
  theme: {
32477
32477
  background: 'var(--ion-background-color)',
32478
32478
  activeColor: 'primary',
@@ -32647,7 +32647,7 @@ class BottomNavComponent {
32647
32647
  return 'none';
32648
32648
  case undefined:
32649
32649
  case '':
32650
- return 'var(--val-container-xl, 1100px)';
32650
+ return 'var(--val-container-md, 720px)';
32651
32651
  default:
32652
32652
  return v;
32653
32653
  }
@@ -32773,7 +32773,7 @@ class BottomNavComponent {
32773
32773
  }
32774
32774
  </div>
32775
32775
  </nav>
32776
- `, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-xl, 1100px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
32776
+ `, isInline: true, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "component", type: IonIcon, selector: "ion-icon", inputs: ["color", "flipRtl", "icon", "ios", "lazy", "md", "mode", "name", "sanitize", "size", "src"] }, { kind: "component", type: IonRippleEffect, selector: "ion-ripple-effect", inputs: ["type"] }] }); }
32777
32777
  }
32778
32778
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: BottomNavComponent, decorators: [{
32779
32779
  type: Component,
@@ -32847,7 +32847,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
32847
32847
  }
32848
32848
  </div>
32849
32849
  </nav>
32850
- `, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-xl, 1100px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
32850
+ `, styles: [":host{display:block;position:fixed;bottom:0;left:0;right:0;z-index:100;pointer-events:none}.bottom-nav{--bottom-nav-bg: var(--ion-background-color);--bottom-nav-active: var(--ion-color-primary);--bottom-nav-inactive: var(--ion-color-medium);--bottom-nav-radius: 16px 16px 0 0;--bottom-nav-height: 64px;--bottom-nav-fab-size: 56px;--bottom-nav-max-width: var(--val-container-md, 720px);--fab-color: var(--ion-color-primary);pointer-events:auto;background:var(--bottom-nav-bg);border-radius:var(--bottom-nav-radius);height:var(--bottom-nav-height);padding:0 8px;max-width:var(--bottom-nav-max-width);margin:0 auto}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000014}.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.75);backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border-top:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.06)}.bottom-nav--floating{margin:0 auto 8px;width:calc(100% - 32px);border-radius:32px;border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08);box-shadow:0 4px 24px #00000014,0 8px 32px #0000000a;--bottom-nav-radius: 32px}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 255, 255, 255),.82);backdrop-filter:blur(28px) saturate(200%);-webkit-backdrop-filter:blur(28px) saturate(200%);border:1px solid rgba(var(--ion-text-color-rgb, 0, 0, 0),.08)}.bottom-nav--floating.bottom-nav--elevated{box-shadow:0 4px 24px #0000001a,0 12px 48px #00000014}.bottom-nav--floating.bottom-nav--safe-area{margin-bottom:calc(8px + env(safe-area-inset-bottom,0))}.bottom-nav--safe-area{padding-bottom:env(safe-area-inset-bottom,0);height:calc(var(--bottom-nav-height) + env(safe-area-inset-bottom,0))}.bottom-nav__container{display:flex;align-items:center;justify-content:space-around;height:var(--bottom-nav-height);max-width:500px;margin:0 auto}.bottom-nav__tab{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:4px;height:100%;padding:8px 4px;background:transparent;border:none;cursor:pointer;position:relative;overflow:hidden;color:var(--bottom-nav-inactive);transition:color .2s ease;max-width:80px;--ripple-color: var(--bottom-nav-active)}.bottom-nav__tab:focus-visible{outline:2px solid var(--bottom-nav-active);outline-offset:-2px;border-radius:8px}.bottom-nav__tab--active{color:var(--bottom-nav-active)}.bottom-nav__tab--disabled{opacity:.4;cursor:not-allowed;pointer-events:none}.bottom-nav__tab[data-animation=scale] .bottom-nav__tab-icon{transition:transform .2s cubic-bezier(.4,0,.2,1)}.bottom-nav__tab[data-animation=scale].bottom-nav__tab--active .bottom-nav__tab-icon{transform:scale(1.15)}.bottom-nav__tab[data-animation=fade]{transition:opacity .2s ease,color .2s ease}.bottom-nav__tab[data-animation=fade]:not(.bottom-nav__tab--active){opacity:.6}.bottom-nav__tab[data-animation=slide] .bottom-nav__tab-label{transition:transform .2s ease,opacity .2s ease;transform:translateY(6px);opacity:0}.bottom-nav__tab[data-animation=slide].bottom-nav__tab--active .bottom-nav__tab-label{transform:translateY(0);opacity:1}.bottom-nav__tab-icon{position:relative;font-size:24px;line-height:1}.bottom-nav__tab-icon ion-icon{display:block}.bottom-nav__tab-label{font-size:11px;font-weight:500;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%;letter-spacing:.01em}.bottom-nav__badge{position:absolute;top:-4px;right:-10px;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:18px;text-align:center;color:#fff;background-color:var(--ion-color-danger);border-radius:9px;box-shadow:0 1px 3px #0003}.bottom-nav__badge--dot{min-width:10px;width:10px;height:10px;padding:0;top:-2px;right:-4px;border-radius:50%}.bottom-nav__fab{position:relative;flex-shrink:0;width:var(--bottom-nav-fab-size);height:var(--bottom-nav-fab-size);margin:0 12px;margin-top:calc(var(--bottom-nav-fab-size) * -.35);background:var(--fab-color);border:none;border-radius:50%;cursor:pointer;display:flex;align-items:center;justify-content:center;color:#fff;font-size:28px;box-shadow:0 4px 14px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.4);transition:transform .2s cubic-bezier(.4,0,.2,1),box-shadow .2s ease;overflow:hidden;--ripple-color: rgba(255, 255, 255, .3)}.bottom-nav__fab:hover{transform:scale(1.08);box-shadow:0 6px 20px rgba(var(--ion-color-primary-rgb, 56, 128, 255),.5)}.bottom-nav__fab:active{transform:scale(.95)}.bottom-nav__fab:focus-visible{outline:3px solid white;outline-offset:2px}.bottom-nav__fab--small{--bottom-nav-fab-size: 48px;font-size:24px;margin-top:-14.4px}.bottom-nav__fab ion-icon{display:block}.bottom-nav--hide-labels{--bottom-nav-height: 56px}.bottom-nav--hide-labels .bottom-nav__tab-label{display:none}.bottom-nav--hide-labels .bottom-nav__tab-icon{font-size:26px}@media (prefers-color-scheme: dark){.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 0, 0, 0),.8);border-top-color:#ffffff14}.bottom-nav--elevated{box-shadow:0 -4px 20px #00000040}.bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}.bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}.bottom-nav__fab{box-shadow:0 4px 14px #0006}.bottom-nav__fab:hover{box-shadow:0 6px 20px #00000080}}:host-context(.dark) .bottom-nav--translucent,:host-context(body.dark) .bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.85);border-top-color:#ffffff14}:host-context(.dark) .bottom-nav--elevated,:host-context(body.dark) .bottom-nav--elevated,:host-context([data-theme=dark]) .bottom-nav--elevated{box-shadow:0 -4px 20px #0000004d}:host-context(.dark) .bottom-nav--floating,:host-context(body.dark) .bottom-nav--floating,:host-context([data-theme=dark]) .bottom-nav--floating{border-color:#ffffff1a;box-shadow:0 4px 24px #0003,0 8px 32px #00000026}:host-context(.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context(body.dark) .bottom-nav--floating.bottom-nav--translucent,:host-context([data-theme=dark]) .bottom-nav--floating.bottom-nav--translucent{background:rgba(var(--ion-background-color-rgb, 18, 18, 18),.88);border-color:#ffffff1a}:host-context(.dark) .bottom-nav__fab,:host-context(body.dark) .bottom-nav__fab,:host-context([data-theme=dark]) .bottom-nav__fab{box-shadow:0 4px 14px #00000080}:host-context(.dark) .bottom-nav__fab:hover,:host-context(body.dark) .bottom-nav__fab:hover,:host-context([data-theme=dark]) .bottom-nav__fab:hover{box-shadow:0 6px 20px #0009}@supports (padding-bottom: env(safe-area-inset-bottom)){.bottom-nav--safe-area .bottom-nav__container{height:var(--bottom-nav-height)}}\n"] }]
32851
32851
  }], propDecorators: { tabClick: [{
32852
32852
  type: Output
32853
32853
  }], fabClick: [{
@@ -34953,7 +34953,8 @@ const ORDERED_RE = /^\s*\d+\.\s+(.+)$/;
34953
34953
  const TABLE_DIVIDER_RE = /^\s*\|?[\s:|-]+\|?\s*$/;
34954
34954
  const TABLE_ROW_RE = /^\s*\|(.+)\|\s*$/;
34955
34955
  /**
34956
- * Converts Markdown documents into ArticleMetadata for the val-article organism.
34956
+ * Pure Markdown ArticleMetadata parser. No Angular deps — usable from Node scripts
34957
+ * (build-time generation) and from the Angular `MarkdownArticleParserService` wrapper.
34957
34958
  *
34958
34959
  * Supported syntax:
34959
34960
  * - Headings (#, ##, ### …) → title / subtitle
@@ -34965,261 +34966,256 @@ const TABLE_ROW_RE = /^\s*\|(.+)\|\s*$/;
34965
34966
  * - Tables → flattened into paragraphs with bold keys
34966
34967
  * - Horizontal rules (---, ***, ___) → separator
34967
34968
  */
34968
- class MarkdownArticleParserService {
34969
- parse(markdown, config) {
34970
- const lines = this.normalize(markdown).split('\n');
34971
- const elements = [];
34972
- let i = 0;
34973
- while (i < lines.length) {
34974
- const line = lines[i];
34975
- if (line.trim() === '') {
34969
+ function parseMarkdownArticle(markdown, config) {
34970
+ const lines = normalize(markdown).split('\n');
34971
+ const elements = [];
34972
+ let i = 0;
34973
+ while (i < lines.length) {
34974
+ const line = lines[i];
34975
+ if (line.trim() === '') {
34976
+ i++;
34977
+ continue;
34978
+ }
34979
+ const fence = line.match(FENCE_RE);
34980
+ if (fence) {
34981
+ const lang = fence[1] || undefined;
34982
+ const body = [];
34983
+ i++;
34984
+ while (i < lines.length && !FENCE_RE.test(lines[i])) {
34985
+ body.push(lines[i]);
34976
34986
  i++;
34977
- continue;
34978
34987
  }
34979
- // Fenced code block
34980
- const fence = line.match(FENCE_RE);
34981
- if (fence) {
34982
- const lang = fence[1] || undefined;
34983
- const body = [];
34988
+ i++;
34989
+ elements.push({
34990
+ type: 'code',
34991
+ props: { code: body.join('\n'), language: lang, theme: 'dark' },
34992
+ });
34993
+ continue;
34994
+ }
34995
+ if (BOX_DRAWING.test(line)) {
34996
+ const body = [];
34997
+ while (i < lines.length && (lines[i].trim() === '' || BOX_DRAWING.test(lines[i]))) {
34998
+ body.push(lines[i]);
34984
34999
  i++;
34985
- while (i < lines.length && !FENCE_RE.test(lines[i])) {
34986
- body.push(lines[i]);
34987
- i++;
34988
- }
34989
- i++; // skip closing fence
34990
- elements.push({
34991
- type: 'code',
34992
- props: { code: body.join('\n'), language: lang, theme: 'dark' },
34993
- });
34994
- continue;
34995
35000
  }
34996
- // ASCII box-drawing art treat as code block
34997
- if (BOX_DRAWING.test(line)) {
34998
- const body = [];
34999
- while (i < lines.length && (lines[i].trim() === '' || BOX_DRAWING.test(lines[i]))) {
35000
- body.push(lines[i]);
35001
- i++;
35002
- }
35003
- // Trim trailing blank lines from the block
35004
- while (body.length && body[body.length - 1].trim() === '')
35005
- body.pop();
35006
- elements.push({
35007
- type: 'code',
35008
- props: { code: body.join('\n'), language: 'text', theme: 'dark' },
35009
- });
35010
- continue;
35011
- }
35012
- // Separator
35013
- if (SEPARATOR_RE.test(line)) {
35014
- elements.push({ type: 'separator', props: { style: 'line' } });
35001
+ while (body.length && body[body.length - 1].trim() === '')
35002
+ body.pop();
35003
+ elements.push({
35004
+ type: 'code',
35005
+ props: { code: body.join('\n'), language: 'text', theme: 'dark' },
35006
+ });
35007
+ continue;
35008
+ }
35009
+ if (SEPARATOR_RE.test(line)) {
35010
+ elements.push({ type: 'separator', props: { style: 'line' } });
35011
+ i++;
35012
+ continue;
35013
+ }
35014
+ const heading = line.match(HEADING_RE);
35015
+ if (heading) {
35016
+ elements.push(makeHeading(heading[1].length, heading[2].trim()));
35017
+ i++;
35018
+ continue;
35019
+ }
35020
+ if (line.startsWith('>')) {
35021
+ const block = [];
35022
+ while (i < lines.length && lines[i].startsWith('>')) {
35023
+ block.push(lines[i]);
35015
35024
  i++;
35016
- continue;
35017
35025
  }
35018
- // Heading
35019
- const heading = line.match(HEADING_RE);
35020
- if (heading) {
35021
- elements.push(this.makeHeading(heading[1].length, heading[2].trim()));
35026
+ elements.push(makeQuoteOrCallout(block));
35027
+ continue;
35028
+ }
35029
+ if (TABLE_ROW_RE.test(line)) {
35030
+ const rows = [];
35031
+ while (i < lines.length && TABLE_ROW_RE.test(lines[i])) {
35032
+ rows.push(lines[i]);
35022
35033
  i++;
35023
- continue;
35024
- }
35025
- // Callout / blockquote (consume contiguous `>` lines)
35026
- if (line.startsWith('>')) {
35027
- const block = [];
35028
- while (i < lines.length && lines[i].startsWith('>')) {
35029
- block.push(lines[i]);
35030
- i++;
35031
- }
35032
- elements.push(this.makeQuoteOrCallout(block));
35033
- continue;
35034
35034
  }
35035
- // Table (consume contiguous `|` lines)
35036
- if (TABLE_ROW_RE.test(line)) {
35037
- const rows = [];
35038
- while (i < lines.length && TABLE_ROW_RE.test(lines[i])) {
35039
- rows.push(lines[i]);
35040
- i++;
35041
- }
35042
- elements.push(...this.makeTable(rows));
35043
- continue;
35044
- }
35045
- // List (checklist / unordered / ordered) consume contiguous lines
35046
- if (CHECKLIST_RE.test(line) || UNORDERED_RE.test(line) || ORDERED_RE.test(line)) {
35047
- const listType = CHECKLIST_RE.test(line)
35048
- ? 'checklist'
35049
- : ORDERED_RE.test(line)
35050
- ? 'ordered'
35051
- : 'unordered';
35052
- const items = [];
35053
- while (i < lines.length && lines[i].trim() !== '') {
35054
- const cur = lines[i];
35055
- const check = cur.match(CHECKLIST_RE);
35056
- const unord = cur.match(UNORDERED_RE);
35057
- const ord = cur.match(ORDERED_RE);
35058
- if (check)
35059
- items.push({ text: check[2].trim() });
35060
- else if (ord && listType === 'ordered')
35061
- items.push({ text: ord[1].trim() });
35062
- else if (unord && listType !== 'ordered')
35063
- items.push({ text: unord[1].trim() });
35064
- else
35065
- break;
35066
- i++;
35067
- }
35068
- elements.push({ type: 'list', props: { items, listType } });
35069
- continue;
35070
- }
35071
- // Paragraph — consume contiguous non-empty lines that aren't a special block
35072
- const paragraph = [];
35073
- while (i < lines.length && lines[i].trim() !== '' && !this.startsNewBlock(lines[i])) {
35074
- paragraph.push(lines[i]);
35035
+ elements.push(...makeTable(rows));
35036
+ continue;
35037
+ }
35038
+ if (CHECKLIST_RE.test(line) || UNORDERED_RE.test(line) || ORDERED_RE.test(line)) {
35039
+ const listType = CHECKLIST_RE.test(line)
35040
+ ? 'checklist'
35041
+ : ORDERED_RE.test(line)
35042
+ ? 'ordered'
35043
+ : 'unordered';
35044
+ const items = [];
35045
+ while (i < lines.length && lines[i].trim() !== '') {
35046
+ const cur = lines[i];
35047
+ const check = cur.match(CHECKLIST_RE);
35048
+ const unord = cur.match(UNORDERED_RE);
35049
+ const ord = cur.match(ORDERED_RE);
35050
+ if (check)
35051
+ items.push({ text: check[2].trim() });
35052
+ else if (ord && listType === 'ordered')
35053
+ items.push({ text: ord[1].trim() });
35054
+ else if (unord && listType !== 'ordered')
35055
+ items.push({ text: unord[1].trim() });
35056
+ else
35057
+ break;
35075
35058
  i++;
35076
35059
  }
35077
- const text = paragraph.join(' ').trim();
35078
- if (text) {
35079
- elements.push({
35080
- type: 'paragraph',
35081
- props: {
35082
- content: text,
35083
- size: 'medium',
35084
- color: 'dark',
35085
- bold: false,
35086
- processLinks: true,
35087
- allowPartialBold: true,
35088
- },
35089
- });
35090
- }
35060
+ elements.push({ type: 'list', props: { items, listType } });
35061
+ continue;
35091
35062
  }
35092
- return {
35093
- elements,
35094
- maxWidth: '900px',
35095
- centered: true,
35096
- theme: 'auto',
35097
- ...config,
35098
- };
35099
- }
35100
- normalize(md) {
35101
- return md.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
35102
- }
35103
- startsNewBlock(line) {
35104
- return (HEADING_RE.test(line) ||
35105
- FENCE_RE.test(line) ||
35106
- SEPARATOR_RE.test(line) ||
35107
- TABLE_ROW_RE.test(line) ||
35108
- UNORDERED_RE.test(line) ||
35109
- ORDERED_RE.test(line) ||
35110
- CHECKLIST_RE.test(line) ||
35111
- line.startsWith('>') ||
35112
- BOX_DRAWING.test(line));
35113
- }
35114
- makeHeading(level, content) {
35115
- if (level === 1) {
35116
- return {
35117
- type: 'title',
35118
- props: { content, size: 'xlarge', color: 'dark', bold: true },
35119
- };
35063
+ const paragraph = [];
35064
+ while (i < lines.length && lines[i].trim() !== '' && !startsNewBlock(lines[i])) {
35065
+ paragraph.push(lines[i]);
35066
+ i++;
35067
+ }
35068
+ const text = paragraph.join(' ').trim();
35069
+ if (text) {
35070
+ elements.push({
35071
+ type: 'paragraph',
35072
+ props: {
35073
+ content: text,
35074
+ size: 'medium',
35075
+ color: 'dark',
35076
+ bold: false,
35077
+ processLinks: true,
35078
+ allowPartialBold: true,
35079
+ },
35080
+ });
35120
35081
  }
35121
- const size = level === 2 ? 'large' : level === 3 ? 'medium' : 'small';
35122
- return {
35123
- type: 'subtitle',
35124
- props: { content, size, color: 'dark', bold: true },
35125
- };
35126
- }
35127
- makeQuoteOrCallout(block) {
35128
- const first = block[0];
35129
- const callout = first.match(CALLOUT_RE);
35130
- const lines = block.map(l => l.replace(/^>\s?/, ''));
35131
- if (callout) {
35132
- const type = callout[1].toUpperCase();
35133
- const firstLineRest = callout[2] || '';
35134
- const rest = lines.slice(1).join(' ').trim();
35135
- const text = [firstLineRest, rest].filter(Boolean).join(' ').trim();
35136
- return this.makeNote(type, text);
35137
- }
35138
- const text = lines.join(' ').trim();
35139
- return {
35140
- type: 'quote',
35141
- props: {
35142
- content: text,
35143
- size: 'medium',
35144
- color: 'medium',
35145
- bold: false,
35146
- showQuoteMark: true,
35147
- alignment: 'left',
35148
- },
35149
- };
35150
35082
  }
35151
- makeNote(kind, text) {
35152
- const map = {
35153
- NOTE: { color: 'primary', prefix: 'Nota' },
35154
- TIP: { color: 'success', prefix: 'Tip' },
35155
- INFO: { color: 'tertiary', prefix: 'Info' },
35156
- IMPORTANT: { color: 'warning', prefix: 'Importante' },
35157
- WARNING: { color: 'warning', prefix: 'Atención' },
35158
- CAUTION: { color: 'danger', prefix: 'Precaución' },
35159
- };
35160
- const cfg = map[kind];
35083
+ return {
35084
+ elements,
35085
+ maxWidth: '900px',
35086
+ centered: true,
35087
+ theme: 'auto',
35088
+ ...config,
35089
+ };
35090
+ }
35091
+ function normalize(md) {
35092
+ return md.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
35093
+ }
35094
+ function startsNewBlock(line) {
35095
+ return (HEADING_RE.test(line) ||
35096
+ FENCE_RE.test(line) ||
35097
+ SEPARATOR_RE.test(line) ||
35098
+ TABLE_ROW_RE.test(line) ||
35099
+ UNORDERED_RE.test(line) ||
35100
+ ORDERED_RE.test(line) ||
35101
+ CHECKLIST_RE.test(line) ||
35102
+ line.startsWith('>') ||
35103
+ BOX_DRAWING.test(line));
35104
+ }
35105
+ function makeHeading(level, content) {
35106
+ if (level === 1) {
35161
35107
  return {
35162
- type: 'note',
35163
- props: {
35164
- text,
35165
- prefix: `${cfg.prefix}:`,
35166
- color: cfg.color,
35167
- textColor: 'dark',
35168
- size: 'medium',
35169
- rounded: true,
35170
- },
35108
+ type: 'title',
35109
+ props: { content, size: 'xlarge', color: 'dark', bold: true },
35171
35110
  };
35172
35111
  }
35173
- /**
35174
- * Tables are flattened into a header subtitle (if present) followed by one paragraph per
35175
- * data row using `**col[0]:** col[1] · **col[2]:** col[3] …` format. val-article has no
35176
- * native table element so this preserves the information without breaking the layout.
35177
- */
35178
- makeTable(rows) {
35179
- const parsed = rows
35180
- .filter(r => !TABLE_DIVIDER_RE.test(r))
35181
- .map(r => r
35182
- .trim()
35183
- .replace(/^\|/, '')
35184
- .replace(/\|$/, '')
35185
- .split('|')
35186
- .map(c => c.trim()));
35187
- if (parsed.length === 0)
35188
- return [];
35189
- const header = parsed[0];
35190
- const dataRows = parsed.slice(1);
35191
- if (dataRows.length === 0) {
35192
- return [
35193
- {
35194
- type: 'paragraph',
35195
- props: {
35196
- content: header.join(' · '),
35197
- size: 'medium',
35198
- color: 'dark',
35199
- bold: false,
35200
- processLinks: true,
35201
- allowPartialBold: true,
35202
- },
35203
- },
35204
- ];
35205
- }
35206
- return dataRows.map(row => {
35207
- const pairs = row.map((cell, idx) => {
35208
- const key = header[idx] ?? '';
35209
- return key ? `**${key}:** ${cell}` : cell;
35210
- });
35211
- return {
35112
+ const size = level === 2 ? 'large' : level === 3 ? 'medium' : 'small';
35113
+ return {
35114
+ type: 'subtitle',
35115
+ props: { content, size, color: 'dark', bold: true },
35116
+ };
35117
+ }
35118
+ function makeQuoteOrCallout(block) {
35119
+ const first = block[0];
35120
+ const callout = first.match(CALLOUT_RE);
35121
+ const lines = block.map(l => l.replace(/^>\s?/, ''));
35122
+ if (callout) {
35123
+ const type = callout[1].toUpperCase();
35124
+ const firstLineRest = callout[2] || '';
35125
+ const rest = lines.slice(1).join(' ').trim();
35126
+ const text = [firstLineRest, rest].filter(Boolean).join(' ').trim();
35127
+ return makeNote(type, text);
35128
+ }
35129
+ const text = lines.join(' ').trim();
35130
+ return {
35131
+ type: 'quote',
35132
+ props: {
35133
+ content: text,
35134
+ size: 'medium',
35135
+ color: 'medium',
35136
+ bold: false,
35137
+ showQuoteMark: true,
35138
+ alignment: 'left',
35139
+ },
35140
+ };
35141
+ }
35142
+ function makeNote(kind, text) {
35143
+ const map = {
35144
+ NOTE: { color: 'primary', prefix: 'Nota' },
35145
+ TIP: { color: 'success', prefix: 'Tip' },
35146
+ INFO: { color: 'tertiary', prefix: 'Info' },
35147
+ IMPORTANT: { color: 'warning', prefix: 'Importante' },
35148
+ WARNING: { color: 'warning', prefix: 'Atención' },
35149
+ CAUTION: { color: 'danger', prefix: 'Precaución' },
35150
+ };
35151
+ const cfg = map[kind];
35152
+ return {
35153
+ type: 'note',
35154
+ props: {
35155
+ text,
35156
+ prefix: `${cfg.prefix}:`,
35157
+ color: cfg.color,
35158
+ textColor: 'dark',
35159
+ size: 'medium',
35160
+ rounded: true,
35161
+ },
35162
+ };
35163
+ }
35164
+ function makeTable(rows) {
35165
+ const parsed = rows
35166
+ .filter(r => !TABLE_DIVIDER_RE.test(r))
35167
+ .map(r => r
35168
+ .trim()
35169
+ .replace(/^\|/, '')
35170
+ .replace(/\|$/, '')
35171
+ .split('|')
35172
+ .map(c => c.trim()));
35173
+ if (parsed.length === 0)
35174
+ return [];
35175
+ const header = parsed[0];
35176
+ const dataRows = parsed.slice(1);
35177
+ if (dataRows.length === 0) {
35178
+ return [
35179
+ {
35212
35180
  type: 'paragraph',
35213
35181
  props: {
35214
- content: pairs.join(' · '),
35182
+ content: header.join(' · '),
35215
35183
  size: 'medium',
35216
35184
  color: 'dark',
35217
35185
  bold: false,
35218
35186
  processLinks: true,
35219
35187
  allowPartialBold: true,
35220
35188
  },
35221
- };
35189
+ },
35190
+ ];
35191
+ }
35192
+ return dataRows.map(row => {
35193
+ const pairs = row.map((cell, idx) => {
35194
+ const key = header[idx] ?? '';
35195
+ return key ? `**${key}:** ${cell}` : cell;
35222
35196
  });
35197
+ return {
35198
+ type: 'paragraph',
35199
+ props: {
35200
+ content: pairs.join(' · '),
35201
+ size: 'medium',
35202
+ color: 'dark',
35203
+ bold: false,
35204
+ processLinks: true,
35205
+ allowPartialBold: true,
35206
+ },
35207
+ };
35208
+ });
35209
+ }
35210
+
35211
+ /**
35212
+ * Angular service wrapper for the pure {@link parseMarkdownArticle} function.
35213
+ * Provided in root so it can be injected anywhere. The actual parsing logic lives in
35214
+ * `markdown-article-parser.ts` and is also usable from Node scripts (build-time generation).
35215
+ */
35216
+ class MarkdownArticleParserService {
35217
+ parse(markdown, config) {
35218
+ return parseMarkdownArticle(markdown, config);
35223
35219
  }
35224
35220
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MarkdownArticleParserService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
35225
35221
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: MarkdownArticleParserService, providedIn: 'root' }); }
@@ -35229,43 +35225,77 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
35229
35225
  args: [{ providedIn: 'root' }]
35230
35226
  }] });
35231
35227
 
35228
+ const LEGAL_CONTENT_CONFIG = new InjectionToken('LEGAL_CONTENT_CONFIG');
35232
35229
  /**
35233
- * Loads Markdown legal documents from `/assets/legal/{locale}/{slug}.md` and parses them
35234
- * into ArticleMetadata ready for `<val-article>`. Caches parsed results by `locale:slug`
35235
- * so multiple views consuming the same doc share one HTTP request.
35230
+ * Loads legal articles via one of two modes:
35231
+ *
35232
+ * 1. **Build-time** (preferred): when the app provides `LEGAL_CONTENT_CONFIG.factories`
35233
+ * via `provideLegalContent()`, the service dynamically imports the matching
35234
+ * locale module and returns the pre-parsed `ArticleMetadata` synchronously
35235
+ * (wrapped in an Observable). Each locale is code-split.
35236
+ *
35237
+ * 2. **Runtime**: when no factory matches, falls back to fetching
35238
+ * `/assets/legal/{locale}/{slug}.md` and parsing on the fly.
35239
+ *
35240
+ * Both modes cache by `locale:slug` so concurrent loads share one promise/HTTP request.
35236
35241
  */
35237
35242
  class LegalContentService {
35238
35243
  constructor() {
35239
35244
  this.http = inject(HttpClient);
35240
35245
  this.parser = inject(MarkdownArticleParserService);
35246
+ this.config = inject(LEGAL_CONTENT_CONFIG, { optional: true }) ?? {};
35241
35247
  this.DEFAULT_BASE = '/assets/legal';
35242
35248
  this.cache = new Map();
35249
+ this.factoryCache = new Map();
35243
35250
  }
35244
35251
  load(slug, options = {}) {
35245
35252
  const locale = (options.locale ?? 'es').toLowerCase();
35246
- const base = options.basePath ?? this.DEFAULT_BASE;
35247
- const fallback = options.fallbackLocale === undefined ? 'es' : options.fallbackLocale;
35248
- const key = `${base}|${locale}|${slug}`;
35253
+ const fallback = options.fallbackLocale === undefined
35254
+ ? (this.config.fallbackLocale ?? 'es')
35255
+ : options.fallbackLocale;
35256
+ const key = `${locale}|${slug}`;
35249
35257
  const cached = this.cache.get(key);
35250
35258
  if (cached)
35251
35259
  return cached;
35252
- const primary = this.fetchAndParse(`${base}/${locale}/${slug}.md`);
35260
+ const primary = this.loadOne(slug, locale, options.basePath);
35253
35261
  const stream = fallback && fallback !== locale
35254
- ? primary.pipe(catchError(() => this.fetchAndParse(`${base}/${fallback}/${slug}.md`)))
35262
+ ? primary.pipe(catchError(() => this.loadOne(slug, fallback, options.basePath)))
35255
35263
  : primary;
35256
35264
  const shared = stream.pipe(shareReplay({ bufferSize: 1, refCount: false }));
35257
35265
  this.cache.set(key, shared);
35258
35266
  return shared;
35259
35267
  }
35260
- /** Returns the raw Markdown string without parsing. Useful for debugging. */
35268
+ /** Raw Markdown only available in runtime mode (HTTP). */
35261
35269
  raw(slug, options = {}) {
35262
35270
  const locale = (options.locale ?? 'es').toLowerCase();
35263
- const base = options.basePath ?? this.DEFAULT_BASE;
35271
+ const base = options.basePath ?? this.config.basePath ?? this.DEFAULT_BASE;
35264
35272
  return this.http.get(`${base}/${locale}/${slug}.md`, { responseType: 'text' });
35265
35273
  }
35266
- /** Clears the in-memory cache. Call when the user changes locale at runtime. */
35274
+ /** Clears in-memory caches. Call on runtime locale change. */
35267
35275
  invalidate() {
35268
35276
  this.cache.clear();
35277
+ this.factoryCache.clear();
35278
+ }
35279
+ loadOne(slug, locale, basePathOverride) {
35280
+ const factory = this.config.factories?.[locale];
35281
+ if (factory) {
35282
+ return from(this.runFactory(locale, factory)).pipe(switchMap(content => {
35283
+ const article = content[slug];
35284
+ return article
35285
+ ? of(article)
35286
+ : throwError(() => new Error(`Legal doc not found: ${locale}/${slug}`));
35287
+ }));
35288
+ }
35289
+ const base = basePathOverride ?? this.config.basePath ?? this.DEFAULT_BASE;
35290
+ return this.fetchAndParse(`${base}/${locale}/${slug}.md`);
35291
+ }
35292
+ runFactory(locale, factory) {
35293
+ const cached = this.factoryCache.get(locale);
35294
+ if (cached)
35295
+ return cached;
35296
+ const promise = factory();
35297
+ this.factoryCache.set(locale, promise);
35298
+ return promise;
35269
35299
  }
35270
35300
  fetchAndParse(url) {
35271
35301
  return this.http.get(url, { responseType: 'text' }).pipe(switchMap(md => md && md.trim().length > 0
@@ -35279,6 +35309,250 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
35279
35309
  type: Injectable,
35280
35310
  args: [{ providedIn: 'root' }]
35281
35311
  }] });
35312
+ /**
35313
+ * Wires pre-generated legal content into `LegalContentService`. Call from `main.ts`
35314
+ * (or any `providers: []` array). The factories use dynamic `import()` so each
35315
+ * locale is code-split — only the active locale's bundle is loaded.
35316
+ *
35317
+ * @example
35318
+ * provideLegalContent({
35319
+ * factories: {
35320
+ * es: () => import('./app/generated/legal-content.es').then((m) => m.LEGAL_CONTENT_ES),
35321
+ * en: () => import('./app/generated/legal-content.en').then((m) => m.LEGAL_CONTENT_EN),
35322
+ * pt: () => import('./app/generated/legal-content.pt').then((m) => m.LEGAL_CONTENT_PT),
35323
+ * },
35324
+ * })
35325
+ */
35326
+ function provideLegalContent(config) {
35327
+ return { provide: LEGAL_CONTENT_CONFIG, useValue: config };
35328
+ }
35329
+
35330
+ const VALTECH_LEGAL_CONFIG = new InjectionToken('VALTECH_LEGAL_CONFIG');
35331
+ /**
35332
+ * Resolves legal/site paths against a configurable main-site base URL.
35333
+ *
35334
+ * - **Main site mode**: no `provideValtechLegal` called → `resolve('/legal/terms')` returns `/legal/terms` (relative).
35335
+ * - **Satellite mode**: `provideValtechLegal({ baseUrl: 'https://myvaltech.com' })` → `resolve('/legal/terms')` returns `'https://myvaltech.com/legal/terms'` and `isExternal('/legal/terms')` returns `true`.
35336
+ *
35337
+ * Absolute URLs passed in are returned unchanged (no double-prefix).
35338
+ */
35339
+ class LegalLinkService {
35340
+ constructor() {
35341
+ this.config = inject(VALTECH_LEGAL_CONFIG, { optional: true }) ?? {};
35342
+ }
35343
+ /** Effective base URL (null if running as main site). */
35344
+ get baseUrl() {
35345
+ return this.config.baseUrl?.replace(/\/$/, '') ?? null;
35346
+ }
35347
+ /** Whether resolved external links should open in a new tab. */
35348
+ get openInNewTab() {
35349
+ return this.config.openInNewTab ?? Boolean(this.config.baseUrl);
35350
+ }
35351
+ /**
35352
+ * Returns the URL to use for a given internal path. Absolute URLs pass through.
35353
+ * @example
35354
+ * resolve('/legal/terms') // main site: '/legal/terms'
35355
+ * // satellite: 'https://myvaltech.com/legal/terms'
35356
+ * resolve('https://x.com/y') // unchanged
35357
+ */
35358
+ resolve(path) {
35359
+ if (!path)
35360
+ return path;
35361
+ if (/^https?:\/\//i.test(path))
35362
+ return path;
35363
+ const base = this.baseUrl;
35364
+ if (!base)
35365
+ return path;
35366
+ return `${base}${path.startsWith('/') ? '' : '/'}${path}`;
35367
+ }
35368
+ /** `true` if the path would be resolved to a cross-origin URL. */
35369
+ isExternal(path) {
35370
+ if (/^https?:\/\//i.test(path))
35371
+ return true;
35372
+ return this.baseUrl !== null;
35373
+ }
35374
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LegalLinkService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
35375
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LegalLinkService, providedIn: 'root' }); }
35376
+ }
35377
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: LegalLinkService, decorators: [{
35378
+ type: Injectable,
35379
+ args: [{ providedIn: 'root' }]
35380
+ }] });
35381
+ /**
35382
+ * Wires `LegalLinkService` for satellite apps. Omit in the main site (the one
35383
+ * that hosts the canonical /legal/* routes) so links stay relative.
35384
+ *
35385
+ * @example
35386
+ * // main.ts of showcase / sigify / etc.
35387
+ * provideValtechLegal({
35388
+ * baseUrl: 'https://myvaltech.com',
35389
+ * openInNewTab: true,
35390
+ * }),
35391
+ */
35392
+ function provideValtechLegal(config) {
35393
+ return { provide: VALTECH_LEGAL_CONFIG, useValue: config };
35394
+ }
35395
+
35396
+ /**
35397
+ * PreferencesService — preferencias del user en el doc canónico Firestore
35398
+ * `/apps/{appId}/users/{uid}/preferences/main`.
35399
+ *
35400
+ * Read reactivo (signals) via listener Firestore.
35401
+ * Write via `PUT /v2/auth/preferences` (cliente NUNCA escribe Firestore directo —
35402
+ * ver memoria `feedback_no_direct_firestore_writes`).
35403
+ *
35404
+ * Side-effects automáticos:
35405
+ * - Cuando `theme()` cambia → `ThemeService.Theme = ...`
35406
+ * - Cuando `language()` cambia → `I18nService.setLanguage(...)`
35407
+ *
35408
+ * Auto-bind al user actual via `AuthService.user()`. Sin user (logout) → unbind.
35409
+ */
35410
+ class PreferencesService {
35411
+ constructor(config, firestore, auth, http, themeService, i18n) {
35412
+ this.config = config;
35413
+ this.firestore = firestore;
35414
+ this.auth = auth;
35415
+ this.http = http;
35416
+ this.themeService = themeService;
35417
+ this.i18n = i18n;
35418
+ this._theme = signal('auto');
35419
+ this._language = signal('es');
35420
+ this._notificationsMaster = signal(true);
35421
+ /** `true` después del primer snapshot Firestore. Antes, defaults sin side-effects. */
35422
+ this._synced = signal(false);
35423
+ this.theme = this._theme.asReadonly();
35424
+ this.language = this._language.asReadonly();
35425
+ this.notificationsMaster = this._notificationsMaster.asReadonly();
35426
+ this.synced = this._synced.asReadonly();
35427
+ const destroyRef = inject(DestroyRef);
35428
+ destroyRef.onDestroy(() => this.unbind());
35429
+ // Auto-bind al user actual. effect() corre en injection context (constructor OK).
35430
+ effect(() => {
35431
+ const user = this.auth.user();
35432
+ if (user?.userId) {
35433
+ this.bindToUser(user.userId);
35434
+ }
35435
+ else {
35436
+ this.unbind();
35437
+ }
35438
+ });
35439
+ // Side-effect: aplicar theme local cuando llega snapshot real.
35440
+ effect(() => {
35441
+ if (!this._synced())
35442
+ return;
35443
+ const t = this._theme();
35444
+ if (this.themeService) {
35445
+ this.themeService.Theme = t;
35446
+ }
35447
+ });
35448
+ // Side-effect: aplicar language local cuando llega snapshot real.
35449
+ effect(() => {
35450
+ if (!this._synced())
35451
+ return;
35452
+ const l = this._language();
35453
+ if (this.i18n && this.i18n.lang() !== l) {
35454
+ this.i18n.setLanguage(l);
35455
+ }
35456
+ });
35457
+ }
35458
+ /** Actualiza preferencias via backend. Optimistic UI: aplica local, revierte si falla. */
35459
+ async update(partial) {
35460
+ const url = `${this.config.apiUrl}/v2/auth/preferences`;
35461
+ const prev = {
35462
+ theme: this._theme(),
35463
+ language: this._language(),
35464
+ master: this._notificationsMaster(),
35465
+ };
35466
+ if (partial.theme)
35467
+ this._theme.set(partial.theme);
35468
+ if (partial.language)
35469
+ this._language.set(partial.language);
35470
+ if (partial.notifications && typeof partial.notifications.master === 'boolean') {
35471
+ this._notificationsMaster.set(partial.notifications.master);
35472
+ }
35473
+ try {
35474
+ const res = await firstValueFrom(this.http.put(url, partial));
35475
+ // El listener Firestore es la fuente final, pero alinear ya con la
35476
+ // respuesta del backend acelera UX (especialmente si el listener
35477
+ // tarda en propagar).
35478
+ if (res.theme)
35479
+ this._theme.set(res.theme);
35480
+ if (res.language)
35481
+ this._language.set(res.language);
35482
+ if (typeof res.notifications?.master === 'boolean') {
35483
+ this._notificationsMaster.set(res.notifications.master);
35484
+ }
35485
+ return res;
35486
+ }
35487
+ catch (err) {
35488
+ // Revert optimistic.
35489
+ this._theme.set(prev.theme);
35490
+ this._language.set(prev.language);
35491
+ this._notificationsMaster.set(prev.master);
35492
+ throw err;
35493
+ }
35494
+ }
35495
+ setTheme(theme) {
35496
+ return this.update({ theme });
35497
+ }
35498
+ setLanguage(language) {
35499
+ return this.update({ language });
35500
+ }
35501
+ setNotificationsMaster(enabled) {
35502
+ return this.update({ notifications: { master: enabled } });
35503
+ }
35504
+ bindToUser(userId) {
35505
+ if (this.currentUserId === userId && this.subscription)
35506
+ return;
35507
+ this.unbind();
35508
+ this.currentUserId = userId;
35509
+ // FirestoreService.docChanges prefija automáticamente `apps/{appId}/`
35510
+ // cuando `config.appId` está seteado en ValtechAuthConfig.
35511
+ this.subscription = this.firestore
35512
+ .docChanges(`users/${userId}/preferences`, 'main')
35513
+ .subscribe(doc => {
35514
+ if (!doc) {
35515
+ // Doc no existe aún (user nuevo, sin primer sync). Mantenemos
35516
+ // defaults locales pero NO marcamos synced — los side-effects no
35517
+ // pisan el theme/lang locales con valores arbitrarios.
35518
+ return;
35519
+ }
35520
+ if (doc.theme)
35521
+ this._theme.set(doc.theme);
35522
+ if (doc.language)
35523
+ this._language.set(doc.language);
35524
+ if (doc.notifications && typeof doc.notifications.master === 'boolean') {
35525
+ this._notificationsMaster.set(doc.notifications.master);
35526
+ }
35527
+ this._synced.set(true);
35528
+ });
35529
+ }
35530
+ unbind() {
35531
+ this.subscription?.unsubscribe();
35532
+ this.subscription = undefined;
35533
+ this.currentUserId = undefined;
35534
+ this._synced.set(false);
35535
+ }
35536
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PreferencesService, deps: [{ token: VALTECH_AUTH_CONFIG }, { token: FirestoreService }, { token: AuthService }, { token: i1$8.HttpClient }, { token: ThemeService, optional: true }, { token: I18nService, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); }
35537
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PreferencesService, providedIn: 'root' }); }
35538
+ }
35539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImport: i0, type: PreferencesService, decorators: [{
35540
+ type: Injectable,
35541
+ args: [{ providedIn: 'root' }]
35542
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
35543
+ type: Inject,
35544
+ args: [VALTECH_AUTH_CONFIG]
35545
+ }] }, { type: FirestoreService }, { type: AuthService }, { type: i1$8.HttpClient }, { type: ThemeService, decorators: [{
35546
+ type: Optional
35547
+ }] }, { type: I18nService, decorators: [{
35548
+ type: Optional
35549
+ }] }] });
35550
+
35551
+ /**
35552
+ * Preferences types — Fase 1 schema simple (theme + language + notifications.master).
35553
+ * Doc canónico: /apps/{appId}/users/{uid}/preferences/main
35554
+ * Cliente NUNCA escribe directo — todas las mutaciones via PUT /v2/auth/preferences.
35555
+ */
35282
35556
 
35283
35557
  /**
35284
35558
  * Cross-Platform Version Helpers
@@ -42319,11 +42593,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.14", ngImpo
42319
42593
  * Social media links for Valtech
42320
42594
  */
42321
42595
  const VALTECH_SOCIAL_LINKS = [
42322
- { icon: 'logo-facebook', url: 'https://m.facebook.com/profile.php?id=61557610734470', name: 'Facebook' },
42596
+ {
42597
+ icon: 'logo-facebook',
42598
+ url: 'https://m.facebook.com/profile.php?id=61557610734470',
42599
+ name: 'Facebook',
42600
+ },
42323
42601
  { icon: 'logo-instagram', url: 'https://www.instagram.com/valtechltda/', name: 'Instagram' },
42324
42602
  { icon: 'logo-linkedin', url: 'https://www.linkedin.com/company/valtechltda/', name: 'LinkedIn' },
42325
42603
  { icon: 'logo-twitter', url: 'https://twitter.com/valtechltda', name: 'Twitter' },
42326
- { icon: 'logo-youtube', url: 'https://www.youtube.com/channel/UCuF4FGdTiUXxANx1HS4Wi5Q', name: 'YouTube' },
42604
+ {
42605
+ icon: 'logo-youtube',
42606
+ url: 'https://www.youtube.com/channel/UCuF4FGdTiUXxANx1HS4Wi5Q',
42607
+ name: 'YouTube',
42608
+ },
42327
42609
  { icon: 'logo-tiktok', url: 'https://www.tiktok.com/@valtechltda', name: 'TikTok' },
42328
42610
  ];
42329
42611
  /**
@@ -42340,14 +42622,14 @@ const VALTECH_FOOTER_LOGO = {
42340
42622
  */
42341
42623
  const VALTECH_COMPANY_LINKS = {
42342
42624
  legal: [
42343
- { key: 'aboutUs', url: '/about', external: false },
42344
- { key: 'privacyPolicy', url: '/legal/privacy', external: false },
42345
- { key: 'termsConditions', url: '/legal/terms', external: false },
42625
+ { key: 'aboutUs', url: '/about', kind: 'site', external: false },
42626
+ { key: 'privacyPolicy', url: '/legal/privacy', kind: 'legal', external: false },
42627
+ { key: 'termsConditions', url: '/legal/terms', kind: 'legal', external: false },
42346
42628
  ],
42347
42629
  support: [
42348
- { key: 'contactSupport', url: '/contact', external: false },
42349
- { key: 'faq', url: '/help/faq', external: false },
42350
- { key: 'feedback', url: '/feedback', external: false },
42630
+ { key: 'contactSupport', url: '/contact', kind: 'support', external: false },
42631
+ { key: 'faq', url: '/help/faq', kind: 'support', external: false },
42632
+ { key: 'feedback', url: '/feedback', kind: 'support', external: false },
42351
42633
  ],
42352
42634
  };
42353
42635
  /**
@@ -42397,21 +42679,27 @@ const VALTECH_LANGUAGE_SELECTOR = {
42397
42679
  size: 'default',
42398
42680
  };
42399
42681
  /**
42400
- * Helper to build footer links from company links config
42682
+ * Helper to build footer links from company links config.
42683
+ *
42401
42684
  * @param links - Array of company links
42402
42685
  * @param t - Translation function (key: string) => string
42686
+ * @param resolver - Optional resolver — typically `(link) => ({ url: legalLink.resolve(link.url), external: legalLink.isExternal(link.url) && (link.kind === 'legal' || link.kind === 'site') })` to point cross-app legal/site links to the main site.
42403
42687
  * @returns Array of link objects ready for FooterLinksMetadata
42404
42688
  */
42405
- function buildFooterLinks(links, t) {
42406
- return links.map(link => ({
42407
- url: link.url,
42408
- text: t(link.key),
42409
- color: 'dark',
42410
- token: '',
42411
- download: false,
42412
- hoverable: true,
42413
- ...(link.external ? { target: '_blank' } : {}),
42414
- }));
42689
+ function buildFooterLinks(links, t, resolver) {
42690
+ return links.map(link => {
42691
+ const resolved = resolver ? resolver(link) : { url: link.url, external: link.external };
42692
+ const external = resolved.external ?? link.external;
42693
+ return {
42694
+ url: resolved.url,
42695
+ text: t(link.key),
42696
+ color: 'dark',
42697
+ token: '',
42698
+ download: false,
42699
+ hoverable: true,
42700
+ ...(external ? { target: '_blank' } : {}),
42701
+ };
42702
+ });
42415
42703
  }
42416
42704
 
42417
42705
  /**
@@ -42427,5 +42715,5 @@ function buildFooterLinks(links, t) {
42427
42715
  * Generated bundle index. Do not edit.
42428
42716
  */
42429
42717
 
42430
- export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentTransformer, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_SOCIAL_LINKS, VERSION, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, permissionGuard, permissionGuardFromRoute, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
42718
+ export { ACTION_CARD_DEFAULTS, AD_SIZE_MAP, API_TABLE_COLUMN_LABELS, ARTICLE_SPACING, AVATAR_UPLOAD_DEFAULTS, AccordionComponent, ActionCardComponent, ActionHeaderComponent, ActionType, AdSlotComponent, AdsLoaderService, AdsService, AlertBoxComponent, AnalyticsErrorHandler, AnalyticsRouterTracker, AnalyticsService, AppConfigService, ArticleBuilder, ArticleComponent, AuthBackgroundComponent, AuthService, AuthStateService, AuthStorageService, AuthSyncService, AvatarComponent, AvatarUploadComponent, BOTTOM_NAV_DEFAULTS, BannerComponent, BaseDefault, BlogPostBuilder, BottomNavComponent, BoxComponent, BreadcrumbComponent, ButtonComponent, ButtonGroupComponent, COMMON_COUNTRY_CODES, COMMON_CURRENCIES, CURRENCY_INFO, CardComponent, CardSection, CardType, CardsCarouselComponent, CheckInputComponent, CheckboxRadioInputComponent, ChipGroupComponent, ClearDefault, ClearDefaultBlock, ClearDefaultFull, ClearDefaultRound, ClearDefaultRoundBlock, ClearDefaultRoundFull, CodeDisplayComponent, CommandDisplayComponent, CommentComponent, CommentInputComponent, CommentSectionComponent, CompanyFooterComponent, ComponentStates, ConfirmationDialogService, ContainerComponent, ContentLoaderComponent, ContentReactionComponent, ContentTransformer, CountdownComponent, CurrencyInputComponent, DEFAULT_ADS_CONFIG, DEFAULT_APP_CONFIG_SERVICE_CONFIG, DEFAULT_AUTH_CONFIG, DEFAULT_BACK_HEADER, DEFAULT_CANCEL_BUTTON, DEFAULT_CONFIRM_BUTTON, DEFAULT_COUNTDOWN_LABELS, DEFAULT_COUNTDOWN_LABELS_EN, DEFAULT_EMPTY_STATE, DEFAULT_EMULATOR_CONFIG, DEFAULT_FEEDBACK_CONFIG, DEFAULT_FEEDBACK_TYPE_OPTIONS, DEFAULT_HOME_HEADER, DEFAULT_INFINITE_LIST_METADATA, DEFAULT_MODAL_CANCEL_BUTTON, DEFAULT_MODAL_CONFIRM_BUTTON, DEFAULT_PAGE_SIZE_OPTIONS, DEFAULT_PLATFORMS, DEFAULT_REFRESHER_METADATA, DEFAULT_SKELETON_CONFIG, DataTableComponent, DateInputComponent, DateRangeInputComponent, DetailSkeletonComponent, DeviceService, DisplayComponent, DividerComponent, DocsApiTableComponent, DocsBreadcrumbComponent, DocsBuilder, DocsCalloutComponent, DocsCodeExampleComponent, DocsLayoutComponent, DocsNavLinksComponent, DocsNavigationService, DocsPageComponent, DocsSearchComponent, DocsSectionComponent, DocsShellComponent, DocsSidebarComponent, DocsTocComponent, DownloadService, EmailInputComponent, ExpandableTextComponent, FEATURES_LIST_DEFAULTS, FabComponent, FeaturesListComponent, FeedbackFormComponent, FeedbackService, FileInputComponent, FirebaseService, FirestoreCollectionFactory, FirestoreService, FooterComponent, FooterLinksComponent, FormComponent, FormFooterComponent, FormSkeletonComponent, FunHeaderComponent, GlowCardComponent, GridSkeletonComponent, HANDOFF_ROUTE_PARAM, HANDOFF_TOKEN_PARAM, HandoffService, HeaderComponent, HintComponent, HorizontalScrollComponent, HourInputComponent, HrefComponent, I18nService, IMAGE_DEFAULTS, INITIAL_AUTH_STATE, INITIAL_MFA_STATE, Icon, IconComponent, IconService, ImageComponent, ImageCropComponent, ImageService, InAppBrowserService, InfiniteListComponent, InfoComponent, InputI18nHelper, InputType, ItemListComponent, LANG_STORAGE_KEY$1 as LANG_STORAGE_KEY, LEGAL_CONTENT_CONFIG, LOGIN_DEFAULTS, LanguageSelectorComponent, LayeredCardComponent, LegalContentService, LegalLinkService, LinkComponent, LinkProcessorService, LinkedProvidersComponent, LinksAccordionComponent, LinksCakeComponent, ListSkeletonComponent, LoadingDirective, LocalStorageService, LocaleService, LoginComponent, MODAL_SIZES, MOTION, MaintenancePageComponent, MarkdownArticleParserService, MenuComponent, MessagingService, MetaService, ModalService, MultiSelectSearchComponent, NavigationService, NewsBuilder, NoContentComponent, NotesBoxComponent, NotificationActionService, NotificationsService, NumberFromToComponent, NumberInputComponent, NumberStepperComponent, OAUTH_PROVIDERS_INFO, OAuthCallbackComponent, OAuthService, OrgSwitchService, OutlineDefault, OutlineDefaultBlock, OutlineDefaultFull, OutlineDefaultRound, OutlineDefaultRoundBlock, OutlineDefaultRoundFull, PLATFORM_CONFIGS, PageContentComponent, PageTemplateComponent, PageWrapperComponent, PaginationComponent, PaginationService, PasswordInputComponent, PhoneInputComponent, PillComponent, PinInputComponent, PlainCodeBoxComponent, PopoverSelectorComponent, PreferencesService, PresetService, PriceTagComponent, PrimarySolidBlockButton, PrimarySolidBlockHrefButton, PrimarySolidBlockIconButton, PrimarySolidBlockIconHrefButton, PrimarySolidDefaultRoundButton, PrimarySolidDefaultRoundHrefButton, PrimarySolidDefaultRoundIconButton, PrimarySolidDefaultRoundIconHrefButton, PrimarySolidFullButton, PrimarySolidFullHrefButton, PrimarySolidFullIconButton, PrimarySolidFullIconHrefButton, PrimarySolidLargeRoundButton, PrimarySolidLargeRoundHrefButton, PrimarySolidLargeRoundIconButton, PrimarySolidLargeRoundIconHrefButton, PrimarySolidSmallRoundButton, PrimarySolidSmallRoundHrefButton, PrimarySolidSmallRoundIconButton, PrimarySolidSmallRoundIconHrefButton, ProcessLinksPipe, ProfileSkeletonComponent, ProgressBarComponent, ProgressRingComponent, ProgressStatusComponent, PrompterComponent, QR_PRESETS, QrCodeComponent, QrGeneratorService, QueryBuilder, QuoteBoxComponent, RadioInputComponent, RangeInputComponent, RatingComponent, RefresherComponent, RightsFooterComponent, RotatingTextComponent, SKELETON_PRESETS, SearchSelectorComponent, SearchbarComponent, SecondarySolidBlockButton, SecondarySolidBlockHrefButton, SecondarySolidBlockIconButton, SecondarySolidBlockIconHrefButton, SecondarySolidDefaultRoundButton, SecondarySolidDefaultRoundHrefButton, SecondarySolidDefaultRoundIconButton, SecondarySolidDefaultRoundIconHrefButton, SecondarySolidFullButton, SecondarySolidFullHrefButton, SecondarySolidFullIconButton, SecondarySolidFullIconHrefButton, SecondarySolidLargeRoundButton, SecondarySolidLargeRoundHrefButton, SecondarySolidLargeRoundIconButton, SecondarySolidLargeRoundIconHrefButton, SecondarySolidSmallRoundButton, SecondarySolidSmallRoundHrefButton, SecondarySolidSmallRoundIconButton, SecondarySolidSmallRoundIconHrefButton, SegmentControlComponent, SelectSearchComponent, SessionService, ShareButtonsComponent, SimpleComponent, SkeletonComponent, SkeletonService, SolidBlockButton, SolidDefault, SolidDefaultBlock, SolidDefaultButton, SolidDefaultFull, SolidDefaultRound, SolidDefaultRoundBlock, SolidDefaultRoundButton, SolidDefaultRoundFull, SolidFullButton, SolidLargeButton, SolidLargeRoundButton, SolidSmallButton, SolidSmallRoundButton, StatsCardComponent, StepperComponent, StorageService, SwipeCarouselComponent, TabbedContentComponent, TableSkeletonComponent, TabsComponent, Terminal404Component, TestimonialCardComponent, TestimonialCarouselComponent, TextComponent, TextInputComponent, TextareaInputComponent, ThemeOption, ThemeService, TimelineComponent, TitleBlockComponent, TitleComponent, ToastService, ToggleInputComponent, TokenService, ToolbarActionType, ToolbarComponent, TranslatePipe, TypedCollection, UpdateBannerComponent, UsernameInputComponent, VALTECH_ADS_CONFIG, VALTECH_APP_CONFIG, VALTECH_AUTH_CONFIG, VALTECH_COMPANY_LINKS, VALTECH_DEFAULT_CONTENT, VALTECH_FEEDBACK_CONFIG, VALTECH_FIREBASE_CONFIG, VALTECH_FOOTER_I18N, VALTECH_FOOTER_LOGO, VALTECH_LANGUAGE_SELECTOR, VALTECH_LEGAL_CONFIG, VALTECH_SOCIAL_LINKS, VERSION, WizardComponent, WizardFooterComponent, applyDefaultValueToControl, authGuard, authInterceptor, blogPost, buildFooterLinks, buildPath, collections, createFirebaseConfig, createGlowCardProps, createInitialPaginationState, createNumberFromToField, createTitleProps, docs, extractPathParams, getAppInfo, getAppVersion, getCollectionPath, getDocumentId, getTimeOfDayKey, goToTop, guestGuard, hasEmulators, isAtEnd, isCollectionPath, isDocumentPath, isEmulatorMode, isValidPath, joinPath, maxLength, news, parseMarkdownArticle, permissionGuard, permissionGuardFromRoute, provideLegalContent, provideValtechAds, provideValtechAppConfig, provideValtechAuth, provideValtechAuthInterceptor, provideValtechFeedback, provideValtechFirebase, provideValtechI18n, provideValtechLegal, provideValtechPresets, provideValtechSkeleton, query, replaceSpecialChars, resolveColor, resolveInputDefaultValue, roleGuard, storagePaths, superAdminGuard, toArticle };
42431
42719
  //# sourceMappingURL=valtech-components.mjs.map