@akcelik/strct 4.22.0 → 4.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1515,13 +1515,79 @@ class StrctLogin {
1515
1515
  ...(ngDevMode ? [{ debugName: "maxWidth" }] : /* istanbul ignore next */ []));
1516
1516
  /** Enable two-panel split layout. */
1517
1517
  split = input(false, { ...(ngDevMode ? { debugName: "split" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1518
+ /**
1519
+ * The aside's built-in art: `network` is the node diagram over the glow and
1520
+ * dot grid; `grid` is the glow and grid alone. Palette tokens only, so it
1521
+ * follows every scheme, and it is decorative — hidden from assistive tech.
1522
+ */
1523
+ art = input('none', /* @ts-ignore */
1524
+ ...(ngDevMode ? [{ debugName: "art" }] : /* istanbul ignore next */ []));
1525
+ /** An icon tile beside the product name. */
1526
+ brandIcon = input('', /* @ts-ignore */
1527
+ ...(ngDevMode ? [{ debugName: "brandIcon" }] : /* istanbul ignore next */ []));
1528
+ /** The product name, in the aside's brand row. */
1529
+ brandName = input('', /* @ts-ignore */
1530
+ ...(ngDevMode ? [{ debugName: "brandName" }] : /* istanbul ignore next */ []));
1531
+ /** The kicker line above the aside's content. */
1532
+ tagline = input('', /* @ts-ignore */
1533
+ ...(ngDevMode ? [{ debugName: "tagline" }] : /* istanbul ignore next */ []));
1534
+ /** The network's nodes: x, y, radius and which pulse phase they follow. */
1535
+ NODES = [
1536
+ { x: 48, y: 96, r: 3, phase: 1 },
1537
+ { x: 142, y: 158, r: 4.5, phase: 2 },
1538
+ { x: 262, y: 118, r: 3, phase: 3 },
1539
+ { x: 104, y: 272, r: 3, phase: 2 },
1540
+ { x: 224, y: 312, r: 4.5, phase: 3 },
1541
+ { x: 300, y: 222, r: 3, phase: 1 },
1542
+ ];
1518
1543
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctLogin, deps: [], target: i0.ɵɵFactoryTarget.Component });
1519
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctLogin, isStandalone: true, selector: "strct-login", inputs: { maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, split: { classPropertyName: "split", publicName: "split", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-login" }, ngImport: i0, template: `
1544
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctLogin, isStandalone: true, selector: "strct-login", inputs: { maxWidth: { classPropertyName: "maxWidth", publicName: "maxWidth", isSignal: true, isRequired: false, transformFunction: null }, split: { classPropertyName: "split", publicName: "split", isSignal: true, isRequired: false, transformFunction: null }, art: { classPropertyName: "art", publicName: "art", isSignal: true, isRequired: false, transformFunction: null }, brandIcon: { classPropertyName: "brandIcon", publicName: "brandIcon", isSignal: true, isRequired: false, transformFunction: null }, brandName: { classPropertyName: "brandName", publicName: "brandName", isSignal: true, isRequired: false, transformFunction: null }, tagline: { classPropertyName: "tagline", publicName: "tagline", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-login" }, ngImport: i0, template: `
1520
1545
  @if (split()) {
1521
1546
  <div class="strct-login__card" [style.max-width.px]="maxWidth()">
1522
- <aside class="strct-login__aside">
1547
+ <aside class="strct-login__aside" [attr.data-art]="art() === 'none' ? null : art()">
1548
+ @if (art() !== 'none') {
1549
+ <!-- Ambient layers, from palette tokens only, so the art follows
1550
+ every scheme. Decorative: hidden from assistive tech. -->
1551
+ <div class="strct-login__glow strct-login__glow--a" aria-hidden="true"></div>
1552
+ <div class="strct-login__glow strct-login__glow--b" aria-hidden="true"></div>
1553
+ <div class="strct-login__matrix" aria-hidden="true"></div>
1554
+ @if (art() === 'network') {
1555
+ <svg class="strct-login__net" viewBox="0 0 340 430" aria-hidden="true">
1556
+ <path
1557
+ class="strct-login__link"
1558
+ d="M48 96 L142 158 L262 118 M142 158 L104 272 L224 312 L300 222 L262 118"
1559
+ />
1560
+ @for (n of NODES; track $index) {
1561
+ <circle
1562
+ class="strct-login__node strct-login__node--p{{ n.phase }}"
1563
+ [attr.cx]="n.x"
1564
+ [attr.cy]="n.y"
1565
+ [attr.r]="n.r"
1566
+ />
1567
+ }
1568
+ </svg>
1569
+ }
1570
+ }
1523
1571
  <div class="strct-login__aside-inner">
1524
- <ng-content select="[strctLoginAside]" />
1572
+ @if (brandIcon() || brandName()) {
1573
+ <div class="strct-login__brand">
1574
+ @if (brandIcon()) {
1575
+ <span class="strct-login__mark">
1576
+ <strct-icon [name]="brandIcon()" [size]="18" [strokeWidth]="1.5" />
1577
+ </span>
1578
+ }
1579
+ @if (brandName()) {
1580
+ <span class="strct-login__brandname">{{ brandName() }}</span>
1581
+ }
1582
+ </div>
1583
+ }
1584
+ <div class="strct-login__asidebody">
1585
+ @if (tagline()) {
1586
+ <div class="strct-login__kicker">{{ tagline() }}</div>
1587
+ }
1588
+ <ng-content select="[strctLoginAside]" />
1589
+ </div>
1590
+ <div class="strct-login__status"><ng-content select="[strctLoginStatus]" /></div>
1525
1591
  </div>
1526
1592
  </aside>
1527
1593
  <div class="strct-login__main"><ng-content select="[strctLoginMain]" /></div>
@@ -1529,16 +1595,57 @@ class StrctLogin {
1529
1595
  } @else {
1530
1596
  <div class="strct-login__inner" [style.max-width.px]="maxWidth()"><ng-content /></div>
1531
1597
  }
1532
- `, isInline: true, styles: [".strct-login{display:flex;align-items:center;justify-content:center;width:100%;min-height:100%;padding:32px}.strct-login__inner{width:100%}.strct-login__card{display:grid;grid-template-columns:1.05fr 1fr;width:100%;overflow:hidden}.strct-login__main{padding:40px 38px;display:flex;flex-direction:column;justify-content:center}.strct-login__aside{position:relative;overflow:hidden;min-height:420px;color:var(--t1);border-inline-end:1px solid var(--b2)}.strct-login__aside:before,.strct-login__aside:after{display:none}.strct-login__aside-inner{position:relative;z-index:var(--z-base);height:100%;display:flex;flex-direction:column;justify-content:space-between;padding:34px 36px}@media(max-width:720px){.strct-login__card{grid-template-columns:1fr}.strct-login__aside{min-height:220px}.strct-login__main{padding:30px 26px}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1598
+ `, isInline: true, styles: [".strct-login{display:flex;align-items:center;justify-content:center;width:100%;min-height:100%;padding:32px}.strct-login__inner{width:100%}.strct-login__card{display:grid;grid-template-columns:1.05fr 1fr;width:100%;overflow:hidden}.strct-login__main{padding:40px 38px;display:flex;flex-direction:column;justify-content:center}.strct-login__aside{position:relative;overflow:hidden;min-height:420px;color:var(--t1);border-inline-end:1px solid var(--b2)}.strct-login__aside:before,.strct-login__aside:after{display:none}.strct-login__aside-inner{position:relative;z-index:var(--z-base);height:100%;display:flex;flex-direction:column;justify-content:space-between;gap:var(--space-5);padding:34px 36px}.strct-login__glow,.strct-login__matrix,.strct-login__net{position:absolute;inset:-36px;pointer-events:none}.strct-login__glow{filter:blur(46px)}.strct-login__glow--a{background:radial-gradient(360px 300px at 12% 8%,var(--acc30),transparent 70%)}.strct-login__glow--b{background:radial-gradient(320px 300px at 92% 96%,var(--acc18),transparent 70%)}.strct-login__matrix{background-image:radial-gradient(var(--acc30) 1px,transparent 1.4px);background-size:22px 22px;opacity:.4;-webkit-mask-image:linear-gradient(155deg,rgba(0,0,0,.9),transparent 72%);mask-image:linear-gradient(155deg,rgba(0,0,0,.9),transparent 72%)}.strct-login__net{width:calc(100% + 72px);height:calc(100% + 72px)}.strct-login__link{fill:none;stroke:var(--acc30);stroke-width:1}.strct-login__node{fill:var(--acc);opacity:.55}@media(prefers-reduced-motion:no-preference){.strct-login__node{animation:strct-login-pulse 4.5s ease-in-out infinite}.strct-login__node--p2{animation-delay:1.4s}.strct-login__node--p3{animation-delay:2.8s}}@keyframes strct-login-pulse{0%,to{opacity:.35}50%{opacity:.85}}.strct-login__brand{position:relative;display:flex;align-items:center;flex-wrap:wrap;gap:10px}.strct-login__mark{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:9px;background:var(--acc-m);border:1px solid var(--acc30);color:var(--acc)}.strct-login__brandname{font-size:13px;font-weight:700;letter-spacing:2px;color:var(--t1)}.strct-login__asidebody{position:relative}.strct-login__kicker{font-size:11.5px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;margin-block-end:10px;color:var(--acc)}.strct-login__status:empty{display:none}.strct-login__status{position:relative}@media(max-width:720px){.strct-login__card{grid-template-columns:1fr}.strct-login__aside{min-height:220px}.strct-login__main{padding:30px 26px}}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1533
1599
  }
1534
1600
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctLogin, decorators: [{
1535
1601
  type: Component,
1536
- args: [{ selector: 'strct-login', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
1602
+ args: [{ selector: 'strct-login', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
1537
1603
  @if (split()) {
1538
1604
  <div class="strct-login__card" [style.max-width.px]="maxWidth()">
1539
- <aside class="strct-login__aside">
1605
+ <aside class="strct-login__aside" [attr.data-art]="art() === 'none' ? null : art()">
1606
+ @if (art() !== 'none') {
1607
+ <!-- Ambient layers, from palette tokens only, so the art follows
1608
+ every scheme. Decorative: hidden from assistive tech. -->
1609
+ <div class="strct-login__glow strct-login__glow--a" aria-hidden="true"></div>
1610
+ <div class="strct-login__glow strct-login__glow--b" aria-hidden="true"></div>
1611
+ <div class="strct-login__matrix" aria-hidden="true"></div>
1612
+ @if (art() === 'network') {
1613
+ <svg class="strct-login__net" viewBox="0 0 340 430" aria-hidden="true">
1614
+ <path
1615
+ class="strct-login__link"
1616
+ d="M48 96 L142 158 L262 118 M142 158 L104 272 L224 312 L300 222 L262 118"
1617
+ />
1618
+ @for (n of NODES; track $index) {
1619
+ <circle
1620
+ class="strct-login__node strct-login__node--p{{ n.phase }}"
1621
+ [attr.cx]="n.x"
1622
+ [attr.cy]="n.y"
1623
+ [attr.r]="n.r"
1624
+ />
1625
+ }
1626
+ </svg>
1627
+ }
1628
+ }
1540
1629
  <div class="strct-login__aside-inner">
1541
- <ng-content select="[strctLoginAside]" />
1630
+ @if (brandIcon() || brandName()) {
1631
+ <div class="strct-login__brand">
1632
+ @if (brandIcon()) {
1633
+ <span class="strct-login__mark">
1634
+ <strct-icon [name]="brandIcon()" [size]="18" [strokeWidth]="1.5" />
1635
+ </span>
1636
+ }
1637
+ @if (brandName()) {
1638
+ <span class="strct-login__brandname">{{ brandName() }}</span>
1639
+ }
1640
+ </div>
1641
+ }
1642
+ <div class="strct-login__asidebody">
1643
+ @if (tagline()) {
1644
+ <div class="strct-login__kicker">{{ tagline() }}</div>
1645
+ }
1646
+ <ng-content select="[strctLoginAside]" />
1647
+ </div>
1648
+ <div class="strct-login__status"><ng-content select="[strctLoginStatus]" /></div>
1542
1649
  </div>
1543
1650
  </aside>
1544
1651
  <div class="strct-login__main"><ng-content select="[strctLoginMain]" /></div>
@@ -1546,8 +1653,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
1546
1653
  } @else {
1547
1654
  <div class="strct-login__inner" [style.max-width.px]="maxWidth()"><ng-content /></div>
1548
1655
  }
1549
- `, host: { class: 'strct-login' }, styles: [".strct-login{display:flex;align-items:center;justify-content:center;width:100%;min-height:100%;padding:32px}.strct-login__inner{width:100%}.strct-login__card{display:grid;grid-template-columns:1.05fr 1fr;width:100%;overflow:hidden}.strct-login__main{padding:40px 38px;display:flex;flex-direction:column;justify-content:center}.strct-login__aside{position:relative;overflow:hidden;min-height:420px;color:var(--t1);border-inline-end:1px solid var(--b2)}.strct-login__aside:before,.strct-login__aside:after{display:none}.strct-login__aside-inner{position:relative;z-index:var(--z-base);height:100%;display:flex;flex-direction:column;justify-content:space-between;padding:34px 36px}@media(max-width:720px){.strct-login__card{grid-template-columns:1fr}.strct-login__aside{min-height:220px}.strct-login__main{padding:30px 26px}}\n"] }]
1550
- }], propDecorators: { maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], split: [{ type: i0.Input, args: [{ isSignal: true, alias: "split", required: false }] }] } });
1656
+ `, host: { class: 'strct-login' }, styles: [".strct-login{display:flex;align-items:center;justify-content:center;width:100%;min-height:100%;padding:32px}.strct-login__inner{width:100%}.strct-login__card{display:grid;grid-template-columns:1.05fr 1fr;width:100%;overflow:hidden}.strct-login__main{padding:40px 38px;display:flex;flex-direction:column;justify-content:center}.strct-login__aside{position:relative;overflow:hidden;min-height:420px;color:var(--t1);border-inline-end:1px solid var(--b2)}.strct-login__aside:before,.strct-login__aside:after{display:none}.strct-login__aside-inner{position:relative;z-index:var(--z-base);height:100%;display:flex;flex-direction:column;justify-content:space-between;gap:var(--space-5);padding:34px 36px}.strct-login__glow,.strct-login__matrix,.strct-login__net{position:absolute;inset:-36px;pointer-events:none}.strct-login__glow{filter:blur(46px)}.strct-login__glow--a{background:radial-gradient(360px 300px at 12% 8%,var(--acc30),transparent 70%)}.strct-login__glow--b{background:radial-gradient(320px 300px at 92% 96%,var(--acc18),transparent 70%)}.strct-login__matrix{background-image:radial-gradient(var(--acc30) 1px,transparent 1.4px);background-size:22px 22px;opacity:.4;-webkit-mask-image:linear-gradient(155deg,rgba(0,0,0,.9),transparent 72%);mask-image:linear-gradient(155deg,rgba(0,0,0,.9),transparent 72%)}.strct-login__net{width:calc(100% + 72px);height:calc(100% + 72px)}.strct-login__link{fill:none;stroke:var(--acc30);stroke-width:1}.strct-login__node{fill:var(--acc);opacity:.55}@media(prefers-reduced-motion:no-preference){.strct-login__node{animation:strct-login-pulse 4.5s ease-in-out infinite}.strct-login__node--p2{animation-delay:1.4s}.strct-login__node--p3{animation-delay:2.8s}}@keyframes strct-login-pulse{0%,to{opacity:.35}50%{opacity:.85}}.strct-login__brand{position:relative;display:flex;align-items:center;flex-wrap:wrap;gap:10px}.strct-login__mark{display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;border-radius:9px;background:var(--acc-m);border:1px solid var(--acc30);color:var(--acc)}.strct-login__brandname{font-size:13px;font-weight:700;letter-spacing:2px;color:var(--t1)}.strct-login__asidebody{position:relative}.strct-login__kicker{font-size:11.5px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;margin-block-end:10px;color:var(--acc)}.strct-login__status:empty{display:none}.strct-login__status{position:relative}@media(max-width:720px){.strct-login__card{grid-template-columns:1fr}.strct-login__aside{min-height:220px}.strct-login__main{padding:30px 26px}}\n"] }]
1657
+ }], propDecorators: { maxWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxWidth", required: false }] }], split: [{ type: i0.Input, args: [{ isSignal: true, alias: "split", required: false }] }], art: [{ type: i0.Input, args: [{ isSignal: true, alias: "art", required: false }] }], brandIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandIcon", required: false }] }], brandName: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandName", required: false }] }], tagline: [{ type: i0.Input, args: [{ isSignal: true, alias: "tagline", required: false }] }] } });
1551
1658
 
1552
1659
  /** Marks the breadcrumb row projected above the title. */
1553
1660
  class StrctPageHeaderCrumbs {
@@ -23734,6 +23841,813 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
23734
23841
  }, styles: [".strct-windock{display:inline-flex;align-items:center;gap:6px}.strct-windock__chip{display:inline-flex;align-items:center;gap:2px;padding-inline:8px 4px;border:1px solid var(--b2);border-radius:var(--radius-full, 999px);background:var(--bg-1);font-size:12px}.strct-windock__open,.strct-windock__close{border:0;background:none;color:inherit;font:inherit;cursor:pointer;padding:3px 2px;border-radius:var(--radius-sm)}.strct-windock__open:hover{color:var(--acc)}.strct-windock__close{display:inline-flex;color:var(--t3)}.strct-windock__close:hover{color:var(--critical)}.strct-windock__open:focus-visible,.strct-windock__close:focus-visible{outline:2px solid var(--acc50);outline-offset:1px}\n"] }]
23735
23842
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }] } });
23736
23843
 
23844
+ /** A card the message carries — an action to approve, a result to read. */
23845
+ class StrctChatAttachment {
23846
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatAttachment, deps: [], target: i0.ɵɵFactoryTarget.Directive });
23847
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.4", type: StrctChatAttachment, isStandalone: true, selector: "[strctChatAttachment]", host: { classAttribute: "strct-chat__attachment" }, ngImport: i0 });
23848
+ }
23849
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatAttachment, decorators: [{
23850
+ type: Directive,
23851
+ args: [{ selector: '[strctChatAttachment]', host: { class: 'strct-chat__attachment' } }]
23852
+ }] });
23853
+ /**
23854
+ * A conversation is a thread of messages from two or more authors, and an
23855
+ * assistant panel is built from the library like every other panel.
23856
+ *
23857
+ * <strct-chat-thread [busy]="thinking()" label="Assistant conversation">
23858
+ * …<strct-chat-message author="user">…</strct-chat-message>…
23859
+ * </strct-chat-thread>
23860
+ *
23861
+ * The thread is a `role="log"` with `aria-live="polite"`: a streaming reply is
23862
+ * announced once, when it finishes, rather than token by token.
23863
+ */
23864
+ class StrctChatThread {
23865
+ /** The assistant is composing: shows the typing indicator after the last message. */
23866
+ busy = input(false, { ...(ngDevMode ? { debugName: "busy" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
23867
+ /** The thread's accessible name. */
23868
+ label = input('Conversation', /* @ts-ignore */
23869
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
23870
+ /** What the typing indicator says to assistive tech. */
23871
+ typingLabel = input('Assistant is typing', /* @ts-ignore */
23872
+ ...(ngDevMode ? [{ debugName: "typingLabel" }] : /* istanbul ignore next */ []));
23873
+ /** Keep the latest message in view — unless the reader scrolled up. */
23874
+ autoScroll = input(true, { ...(ngDevMode ? { debugName: "autoScroll" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
23875
+ host = inject(ElementRef);
23876
+ /** False once the reader scrolls away from the end; true again at the end. */
23877
+ atEnd = signal(true, /* @ts-ignore */
23878
+ ...(ngDevMode ? [{ debugName: "atEnd" }] : /* istanbul ignore next */ []));
23879
+ constructor() {
23880
+ afterNextRender(() => this.scrollToEnd());
23881
+ effect(() => {
23882
+ this.busy();
23883
+ if (this.autoScroll() && this.atEnd())
23884
+ queueMicrotask(() => this.scrollToEnd());
23885
+ });
23886
+ }
23887
+ onScroll() {
23888
+ const el = this.host.nativeElement;
23889
+ this.atEnd.set(el.scrollHeight - el.scrollTop - el.clientHeight < 24);
23890
+ }
23891
+ /** Jump to the newest message. */
23892
+ scrollToEnd() {
23893
+ const el = this.host.nativeElement;
23894
+ el.scrollTop = el.scrollHeight;
23895
+ }
23896
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatThread, deps: [], target: i0.ɵɵFactoryTarget.Component });
23897
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctChatThread, isStandalone: true, selector: "strct-chat-thread", inputs: { busy: { classPropertyName: "busy", publicName: "busy", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, typingLabel: { classPropertyName: "typingLabel", publicName: "typingLabel", isSignal: true, isRequired: false, transformFunction: null }, autoScroll: { classPropertyName: "autoScroll", publicName: "autoScroll", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "log", "aria-live": "polite", "aria-relevant": "additions" }, listeners: { "scroll": "onScroll()" }, properties: { "attr.aria-label": "label()", "attr.aria-busy": "busy() ? \"true\" : null" }, classAttribute: "strct-chat" }, ngImport: i0, template: `
23898
+ <div class="strct-chat__list" #list><ng-content /></div>
23899
+ @if (busy()) {
23900
+ <div class="strct-chat__typing" [attr.aria-label]="typingLabel()" role="status">
23901
+ <span class="strct-chat__dot"></span>
23902
+ <span class="strct-chat__dot"></span>
23903
+ <span class="strct-chat__dot"></span>
23904
+ </div>
23905
+ }
23906
+ `, isInline: true, styles: [".strct-chat{display:flex;flex-direction:column;gap:var(--space-3);min-height:0;overflow:auto;padding:var(--space-3)}.strct-chat__list{display:flex;flex-direction:column;gap:var(--space-3)}.strct-chat__typing{display:inline-flex;align-items:center;gap:4px;padding-inline-start:38px}.strct-chat__dot{width:6px;height:6px;border-radius:50%;background:var(--t3);animation:strct-chat-bounce 1.1s ease-in-out infinite}.strct-chat__dot:nth-child(2){animation-delay:.15s}.strct-chat__dot:nth-child(3){animation-delay:.3s}@keyframes strct-chat-bounce{0%,70%,to{opacity:.35;transform:translateY(0)}35%{opacity:1;transform:translateY(-3px)}}@media(prefers-reduced-motion:reduce){.strct-chat__dot{animation:none;opacity:.55}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
23907
+ }
23908
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatThread, decorators: [{
23909
+ type: Component,
23910
+ args: [{ selector: 'strct-chat-thread', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
23911
+ <div class="strct-chat__list" #list><ng-content /></div>
23912
+ @if (busy()) {
23913
+ <div class="strct-chat__typing" [attr.aria-label]="typingLabel()" role="status">
23914
+ <span class="strct-chat__dot"></span>
23915
+ <span class="strct-chat__dot"></span>
23916
+ <span class="strct-chat__dot"></span>
23917
+ </div>
23918
+ }
23919
+ `, host: {
23920
+ class: 'strct-chat',
23921
+ role: 'log',
23922
+ 'aria-live': 'polite',
23923
+ 'aria-relevant': 'additions',
23924
+ '[attr.aria-label]': 'label()',
23925
+ '[attr.aria-busy]': 'busy() ? "true" : null',
23926
+ '(scroll)': 'onScroll()',
23927
+ }, styles: [".strct-chat{display:flex;flex-direction:column;gap:var(--space-3);min-height:0;overflow:auto;padding:var(--space-3)}.strct-chat__list{display:flex;flex-direction:column;gap:var(--space-3)}.strct-chat__typing{display:inline-flex;align-items:center;gap:4px;padding-inline-start:38px}.strct-chat__dot{width:6px;height:6px;border-radius:50%;background:var(--t3);animation:strct-chat-bounce 1.1s ease-in-out infinite}.strct-chat__dot:nth-child(2){animation-delay:.15s}.strct-chat__dot:nth-child(3){animation-delay:.3s}@keyframes strct-chat-bounce{0%,70%,to{opacity:.35;transform:translateY(0)}35%{opacity:1;transform:translateY(-3px)}}@media(prefers-reduced-motion:reduce){.strct-chat__dot{animation:none;opacity:.55}}\n"] }]
23928
+ }], ctorParameters: () => [], propDecorators: { busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], typingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "typingLabel", required: false }] }], autoScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoScroll", required: false }] }] } });
23929
+ /**
23930
+ * One message in a {@link StrctChatThread}: an `article` labelled by its
23931
+ * author, with the author's avatar, its content, and any attachment card.
23932
+ */
23933
+ class StrctChatMessage {
23934
+ /** Who wrote it. */
23935
+ author = input('assistant', /* @ts-ignore */
23936
+ ...(ngDevMode ? [{ debugName: "author" }] : /* istanbul ignore next */ []));
23937
+ /** The author's name, shown above the bubble. */
23938
+ name = input('', /* @ts-ignore */
23939
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
23940
+ /** An icon avatar instead of initials — an assistant is an icon. */
23941
+ avatarIcon = input('', /* @ts-ignore */
23942
+ ...(ngDevMode ? [{ debugName: "avatarIcon" }] : /* istanbul ignore next */ []));
23943
+ /** The reply is still arriving: a caret follows the content. */
23944
+ streaming = input(false, { ...(ngDevMode ? { debugName: "streaming" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
23945
+ /** When it was sent. */
23946
+ time = input(null, /* @ts-ignore */
23947
+ ...(ngDevMode ? [{ debugName: "time" }] : /* istanbul ignore next */ []));
23948
+ /** How the time reads (localisable). */
23949
+ timeFormat = input((d) => d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }), /* @ts-ignore */
23950
+ ...(ngDevMode ? [{ debugName: "timeFormat" }] : /* istanbul ignore next */ []));
23951
+ /** Default names per author, overridable with `name` (and localisable). */
23952
+ authorNames = input({
23953
+ user: 'You',
23954
+ assistant: 'Assistant',
23955
+ system: 'System',
23956
+ }, /* @ts-ignore */
23957
+ ...(ngDevMode ? [{ debugName: "authorNames" }] : /* istanbul ignore next */ []));
23958
+ displayName = computed(() => this.name() || this.authorNames()[this.author()], /* @ts-ignore */
23959
+ ...(ngDevMode ? [{ debugName: "displayName" }] : /* istanbul ignore next */ []));
23960
+ shortTime = computed(() => {
23961
+ const t = this.time();
23962
+ return t ? this.timeFormat()(t) : '';
23963
+ }, /* @ts-ignore */
23964
+ ...(ngDevMode ? [{ debugName: "shortTime" }] : /* istanbul ignore next */ []));
23965
+ isoTime = computed(() => this.time()?.toISOString() ?? null, /* @ts-ignore */
23966
+ ...(ngDevMode ? [{ debugName: "isoTime" }] : /* istanbul ignore next */ []));
23967
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatMessage, deps: [], target: i0.ɵɵFactoryTarget.Component });
23968
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctChatMessage, isStandalone: true, selector: "strct-chat-message", inputs: { author: { classPropertyName: "author", publicName: "author", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, avatarIcon: { classPropertyName: "avatarIcon", publicName: "avatarIcon", isSignal: true, isRequired: false, transformFunction: null }, streaming: { classPropertyName: "streaming", publicName: "streaming", isSignal: true, isRequired: false, transformFunction: null }, time: { classPropertyName: "time", publicName: "time", isSignal: true, isRequired: false, transformFunction: null }, timeFormat: { classPropertyName: "timeFormat", publicName: "timeFormat", isSignal: true, isRequired: false, transformFunction: null }, authorNames: { classPropertyName: "authorNames", publicName: "authorNames", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "article" }, properties: { "attr.data-author": "author()", "attr.aria-label": "displayName()", "attr.aria-busy": "streaming() ? \"true\" : null" }, classAttribute: "strct-msg" }, ngImport: i0, template: `
23969
+ @if (author() !== 'system') {
23970
+ <strct-avatar
23971
+ class="strct-msg__avatar"
23972
+ size="sm"
23973
+ [name]="displayName()"
23974
+ [icon]="avatarIcon()"
23975
+ [tone]="author() === 'user' ? 'neutral' : 'accent-soft'"
23976
+ />
23977
+ }
23978
+ <div class="strct-msg__body">
23979
+ @if (author() !== 'system' && (name() || time())) {
23980
+ <div class="strct-msg__meta">
23981
+ @if (name()) {
23982
+ <span class="strct-msg__name">{{ name() }}</span>
23983
+ }
23984
+ @if (time()) {
23985
+ <time class="strct-msg__time" [attr.datetime]="isoTime()">{{ shortTime() }}</time>
23986
+ }
23987
+ </div>
23988
+ }
23989
+ <div class="strct-msg__bubble">
23990
+ <ng-content />
23991
+ @if (streaming()) {
23992
+ <span class="strct-msg__caret" aria-hidden="true"></span>
23993
+ }
23994
+ </div>
23995
+ <ng-content select="[strctChatAttachment]" />
23996
+ </div>
23997
+ `, isInline: true, styles: [".strct-msg{display:flex;align-items:flex-start;gap:var(--space-2);min-width:0}.strct-msg[data-author=user]{flex-direction:row-reverse}.strct-msg[data-author=system]{justify-content:center}.strct-msg__avatar{flex:none}.strct-msg__body{display:flex;flex-direction:column;gap:4px;min-width:0;max-width:80%}.strct-msg[data-author=user] .strct-msg__body{align-items:flex-end}.strct-msg__meta{display:flex;align-items:baseline;gap:6px;font-size:var(--text-sm);color:var(--t3)}.strct-msg__name{font-weight:600}.strct-msg__bubble{padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);color:var(--t1);font-size:13px;line-height:1.5;min-width:0;overflow-wrap:anywhere}.strct-msg[data-author=user] .strct-msg__bubble{background:var(--acc-s);border-color:var(--acc30)}.strct-msg[data-author=system] .strct-msg__bubble{border:0;background:none;color:var(--t3);font-size:var(--text-sm);text-align:center}.strct-msg__caret{display:inline-block;width:2px;height:1em;margin-inline-start:2px;vertical-align:text-bottom;background:var(--acc);animation:strct-chat-caret 1s steps(2,start) infinite}@keyframes strct-chat-caret{to{opacity:0}}@media(prefers-reduced-motion:reduce){.strct-msg__caret{animation:none}}.strct-chat__attachment{display:block;margin-block-start:4px}\n"], dependencies: [{ kind: "component", type: StrctAvatar, selector: "strct-avatar", inputs: ["icon", "shape", "tone", "src", "name", "size", "status"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
23998
+ }
23999
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatMessage, decorators: [{
24000
+ type: Component,
24001
+ args: [{ selector: 'strct-chat-message', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctAvatar], template: `
24002
+ @if (author() !== 'system') {
24003
+ <strct-avatar
24004
+ class="strct-msg__avatar"
24005
+ size="sm"
24006
+ [name]="displayName()"
24007
+ [icon]="avatarIcon()"
24008
+ [tone]="author() === 'user' ? 'neutral' : 'accent-soft'"
24009
+ />
24010
+ }
24011
+ <div class="strct-msg__body">
24012
+ @if (author() !== 'system' && (name() || time())) {
24013
+ <div class="strct-msg__meta">
24014
+ @if (name()) {
24015
+ <span class="strct-msg__name">{{ name() }}</span>
24016
+ }
24017
+ @if (time()) {
24018
+ <time class="strct-msg__time" [attr.datetime]="isoTime()">{{ shortTime() }}</time>
24019
+ }
24020
+ </div>
24021
+ }
24022
+ <div class="strct-msg__bubble">
24023
+ <ng-content />
24024
+ @if (streaming()) {
24025
+ <span class="strct-msg__caret" aria-hidden="true"></span>
24026
+ }
24027
+ </div>
24028
+ <ng-content select="[strctChatAttachment]" />
24029
+ </div>
24030
+ `, host: {
24031
+ class: 'strct-msg',
24032
+ role: 'article',
24033
+ '[attr.data-author]': 'author()',
24034
+ '[attr.aria-label]': 'displayName()',
24035
+ // A streaming reply is announced when it finishes, not token by token.
24036
+ '[attr.aria-busy]': 'streaming() ? "true" : null',
24037
+ }, styles: [".strct-msg{display:flex;align-items:flex-start;gap:var(--space-2);min-width:0}.strct-msg[data-author=user]{flex-direction:row-reverse}.strct-msg[data-author=system]{justify-content:center}.strct-msg__avatar{flex:none}.strct-msg__body{display:flex;flex-direction:column;gap:4px;min-width:0;max-width:80%}.strct-msg[data-author=user] .strct-msg__body{align-items:flex-end}.strct-msg__meta{display:flex;align-items:baseline;gap:6px;font-size:var(--text-sm);color:var(--t3)}.strct-msg__name{font-weight:600}.strct-msg__bubble{padding:var(--space-2) var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);color:var(--t1);font-size:13px;line-height:1.5;min-width:0;overflow-wrap:anywhere}.strct-msg[data-author=user] .strct-msg__bubble{background:var(--acc-s);border-color:var(--acc30)}.strct-msg[data-author=system] .strct-msg__bubble{border:0;background:none;color:var(--t3);font-size:var(--text-sm);text-align:center}.strct-msg__caret{display:inline-block;width:2px;height:1em;margin-inline-start:2px;vertical-align:text-bottom;background:var(--acc);animation:strct-chat-caret 1s steps(2,start) infinite}@keyframes strct-chat-caret{to{opacity:0}}@media(prefers-reduced-motion:reduce){.strct-msg__caret{animation:none}}.strct-chat__attachment{display:block;margin-block-start:4px}\n"] }]
24038
+ }], propDecorators: { author: [{ type: i0.Input, args: [{ isSignal: true, alias: "author", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], avatarIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarIcon", required: false }] }], streaming: [{ type: i0.Input, args: [{ isSignal: true, alias: "streaming", required: false }] }], time: [{ type: i0.Input, args: [{ isSignal: true, alias: "time", required: false }] }], timeFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "timeFormat", required: false }] }], authorNames: [{ type: i0.Input, args: [{ isSignal: true, alias: "authorNames", required: false }] }] } });
24039
+ /**
24040
+ * The box the user writes in: it grows with the text up to `maxRows`, sends on
24041
+ * Enter and breaks a line on Shift+Enter — and never sends mid-composition, so
24042
+ * an IME's Enter commits the candidate instead of the message.
24043
+ */
24044
+ class StrctChatComposer {
24045
+ /** The draft (two-way). */
24046
+ value = model('', /* @ts-ignore */
24047
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
24048
+ /** Placeholder text. */
24049
+ placeholder = input('', /* @ts-ignore */
24050
+ ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
24051
+ /** The field's accessible name (visually hidden). */
24052
+ label = input('Message', /* @ts-ignore */
24053
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
24054
+ /** Disabled while the assistant is busy, say. */
24055
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
24056
+ /** How tall the box grows before it scrolls. */
24057
+ maxRows = input(8, /* @ts-ignore */
24058
+ ...(ngDevMode ? [{ debugName: "maxRows" }] : /* istanbul ignore next */ []));
24059
+ /** The send button's accessible name. */
24060
+ sendLabel = input('Send', /* @ts-ignore */
24061
+ ...(ngDevMode ? [{ debugName: "sendLabel" }] : /* istanbul ignore next */ []));
24062
+ /** The user sent the draft. */
24063
+ send = output();
24064
+ inputId = `strct-composer-${++composerCounter}`;
24065
+ /** True while an IME is composing — Enter must commit, not send. */
24066
+ composing = signal(false, /* @ts-ignore */
24067
+ ...(ngDevMode ? [{ debugName: "composing" }] : /* istanbul ignore next */ []));
24068
+ host = inject(ElementRef);
24069
+ onInput(event) {
24070
+ const el = event.target;
24071
+ this.value.set(el.value);
24072
+ this.grow(el);
24073
+ }
24074
+ /** Grow with the text: reset first, so deleting a line shrinks it again. */
24075
+ grow(el) {
24076
+ el.style.height = 'auto';
24077
+ el.style.height = `${el.scrollHeight}px`;
24078
+ }
24079
+ onEnter(event) {
24080
+ const e = event;
24081
+ if (e.shiftKey || this.composing() || e.isComposing)
24082
+ return;
24083
+ e.preventDefault();
24084
+ this.submit();
24085
+ }
24086
+ submit() {
24087
+ const text = this.value().trim();
24088
+ if (!text || this.disabled())
24089
+ return;
24090
+ this.send.emit(text);
24091
+ this.value.set('');
24092
+ const el = this.host.nativeElement.querySelector('textarea');
24093
+ if (el) {
24094
+ el.value = '';
24095
+ this.grow(el);
24096
+ el.focus();
24097
+ }
24098
+ }
24099
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatComposer, deps: [], target: i0.ɵɵFactoryTarget.Component });
24100
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.4", type: StrctChatComposer, isStandalone: true, selector: "strct-chat-composer", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxRows: { classPropertyName: "maxRows", publicName: "maxRows", isSignal: true, isRequired: false, transformFunction: null }, sendLabel: { classPropertyName: "sendLabel", publicName: "sendLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", send: "send" }, host: { classAttribute: "strct-composer" }, ngImport: i0, template: `
24101
+ <label class="strct-composer__label" [attr.for]="inputId">{{ label() }}</label>
24102
+ <div class="strct-composer__box">
24103
+ <textarea
24104
+ class="strct-composer__input"
24105
+ [id]="inputId"
24106
+ rows="1"
24107
+ [placeholder]="placeholder()"
24108
+ [disabled]="disabled()"
24109
+ [value]="value()"
24110
+ [style.max-height.px]="maxRows() * 20"
24111
+ (input)="onInput($event)"
24112
+ (compositionstart)="composing.set(true)"
24113
+ (compositionend)="composing.set(false)"
24114
+ (keydown.enter)="onEnter($event)"
24115
+ ></textarea>
24116
+ <button
24117
+ type="button"
24118
+ class="strct-composer__send"
24119
+ [attr.aria-label]="sendLabel()"
24120
+ [disabled]="disabled() || !value().trim()"
24121
+ (click)="submit()"
24122
+ >
24123
+ <strct-icon strictName="arrowUp" [size]="15" [strokeWidth]="1.8" />
24124
+ </button>
24125
+ </div>
24126
+ `, isInline: true, styles: [".strct-composer{display:block}.strct-composer__label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-composer__box{display:flex;align-items:flex-end;gap:4px;padding:4px 4px 4px var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);transition:border-color .14s ease,box-shadow .14s ease}.strct-composer__box:focus-within{border-color:var(--acc50);box-shadow:0 0 0 3px var(--acc18);background:var(--bg-1)}.strct-composer__input{flex:1;min-width:0;border:0;background:none;resize:none;padding:6px 0;font-family:var(--font);font-size:13px;line-height:20px;color:var(--t1);overflow-y:auto}.strct-composer__input:focus{outline:none}.strct-composer__input::placeholder{color:var(--t3)}.strct-composer__send{display:inline-flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;border:0;border-radius:var(--radius-md);background:var(--acc);color:var(--inv);cursor:pointer}.strct-composer__send:disabled{opacity:var(--disabled-opacity);cursor:not-allowed}.strct-composer__send:focus-visible{outline:2px solid var(--acc50);outline-offset:2px}\n"], dependencies: [{ kind: "component", type: StrctIcon, selector: "strct-icon", inputs: ["name", "strictName", "size", "strokeWidth", "badge", "ariaLabel", "count", "countMax", "countStatus", "countLabel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
24127
+ }
24128
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctChatComposer, decorators: [{
24129
+ type: Component,
24130
+ args: [{ selector: 'strct-chat-composer', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, imports: [StrctIcon], template: `
24131
+ <label class="strct-composer__label" [attr.for]="inputId">{{ label() }}</label>
24132
+ <div class="strct-composer__box">
24133
+ <textarea
24134
+ class="strct-composer__input"
24135
+ [id]="inputId"
24136
+ rows="1"
24137
+ [placeholder]="placeholder()"
24138
+ [disabled]="disabled()"
24139
+ [value]="value()"
24140
+ [style.max-height.px]="maxRows() * 20"
24141
+ (input)="onInput($event)"
24142
+ (compositionstart)="composing.set(true)"
24143
+ (compositionend)="composing.set(false)"
24144
+ (keydown.enter)="onEnter($event)"
24145
+ ></textarea>
24146
+ <button
24147
+ type="button"
24148
+ class="strct-composer__send"
24149
+ [attr.aria-label]="sendLabel()"
24150
+ [disabled]="disabled() || !value().trim()"
24151
+ (click)="submit()"
24152
+ >
24153
+ <strct-icon strictName="arrowUp" [size]="15" [strokeWidth]="1.8" />
24154
+ </button>
24155
+ </div>
24156
+ `, host: { class: 'strct-composer' }, styles: [".strct-composer{display:block}.strct-composer__label{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.strct-composer__box{display:flex;align-items:flex-end;gap:4px;padding:4px 4px 4px var(--space-3);border:1px solid var(--b2);border-radius:var(--radius-lg);background:var(--bg-2);transition:border-color .14s ease,box-shadow .14s ease}.strct-composer__box:focus-within{border-color:var(--acc50);box-shadow:0 0 0 3px var(--acc18);background:var(--bg-1)}.strct-composer__input{flex:1;min-width:0;border:0;background:none;resize:none;padding:6px 0;font-family:var(--font);font-size:13px;line-height:20px;color:var(--t1);overflow-y:auto}.strct-composer__input:focus{outline:none}.strct-composer__input::placeholder{color:var(--t3)}.strct-composer__send{display:inline-flex;align-items:center;justify-content:center;flex:none;width:28px;height:28px;border:0;border-radius:var(--radius-md);background:var(--acc);color:var(--inv);cursor:pointer}.strct-composer__send:disabled{opacity:var(--disabled-opacity);cursor:not-allowed}.strct-composer__send:focus-visible{outline:2px solid var(--acc50);outline-offset:2px}\n"] }]
24157
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], maxRows: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxRows", required: false }] }], sendLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "sendLabel", required: false }] }], send: [{ type: i0.Output, args: ["send"] }] } });
24158
+ let composerCounter = 0;
24159
+
24160
+ /**
24161
+ * A small, dependency-free QR encoder: byte mode, versions 1–10, the four error
24162
+ * correction levels. Enough for an `otpauth://` URI, which is what the library
24163
+ * needs it for — and nothing more, so it stays readable.
24164
+ *
24165
+ * The maths is the standard one (ISO/IEC 18004): the data is split into
24166
+ * codewords, Reed–Solomon parity is appended per block, the result is laid into
24167
+ * the matrix around the function patterns, and each of the eight masks is
24168
+ * scored so the best one is chosen.
24169
+ */
24170
+ /** Total codewords per version (1–10), index 0 = version 1. */
24171
+ const TOTAL_CODEWORDS = [26, 44, 70, 100, 134, 172, 196, 242, 292, 346];
24172
+ /**
24173
+ * Per version and level: [ec codewords per block, block count group 1,
24174
+ * block count group 2]. Group 2's blocks hold one more data codeword each.
24175
+ */
24176
+ const ECC_TABLE = {
24177
+ L: [
24178
+ [7, 1, 0],
24179
+ [10, 1, 0],
24180
+ [15, 1, 0],
24181
+ [20, 1, 0],
24182
+ [26, 1, 0],
24183
+ [18, 2, 0],
24184
+ [20, 2, 0],
24185
+ [24, 2, 0],
24186
+ [30, 2, 0],
24187
+ [18, 2, 2],
24188
+ ],
24189
+ M: [
24190
+ [10, 1, 0],
24191
+ [16, 1, 0],
24192
+ [26, 1, 0],
24193
+ [18, 2, 0],
24194
+ [24, 2, 0],
24195
+ [16, 4, 0],
24196
+ [18, 4, 0],
24197
+ [22, 2, 2],
24198
+ [22, 3, 2],
24199
+ [26, 4, 1],
24200
+ ],
24201
+ Q: [
24202
+ [13, 1, 0],
24203
+ [22, 1, 0],
24204
+ [18, 2, 0],
24205
+ [26, 2, 0],
24206
+ [18, 2, 2],
24207
+ [24, 4, 0],
24208
+ [18, 2, 4],
24209
+ [22, 4, 2],
24210
+ [20, 4, 4],
24211
+ [24, 6, 2],
24212
+ ],
24213
+ H: [
24214
+ [17, 1, 0],
24215
+ [28, 1, 0],
24216
+ [22, 2, 0],
24217
+ [16, 4, 0],
24218
+ [22, 2, 2],
24219
+ [28, 4, 0],
24220
+ [26, 4, 1],
24221
+ [26, 4, 2],
24222
+ [24, 4, 4],
24223
+ [28, 6, 2],
24224
+ ],
24225
+ };
24226
+ /** Alignment-pattern centres per version (1–10). */
24227
+ const ALIGNMENT = [
24228
+ [],
24229
+ [6, 18],
24230
+ [6, 22],
24231
+ [6, 26],
24232
+ [6, 30],
24233
+ [6, 34],
24234
+ [6, 22, 38],
24235
+ [6, 24, 42],
24236
+ [6, 26, 46],
24237
+ [6, 28, 50],
24238
+ ];
24239
+ /** Format-information bits per (level, mask), already masked per the spec. */
24240
+ const FORMAT_BITS = {
24241
+ L: [0x77c4, 0x72f3, 0x7daa, 0x789d, 0x662f, 0x6318, 0x6c41, 0x6976],
24242
+ M: [0x5412, 0x5125, 0x5e7c, 0x5b4b, 0x45f9, 0x40ce, 0x4f97, 0x4aa0],
24243
+ Q: [0x355f, 0x3068, 0x3f31, 0x3a06, 0x24b4, 0x2183, 0x2eda, 0x2bed],
24244
+ H: [0x1689, 0x13be, 0x1ce7, 0x19d0, 0x0762, 0x0255, 0x0d0c, 0x083b],
24245
+ };
24246
+ // ── Galois field (2^8) with the QR primitive polynomial 0x11d ───────────────
24247
+ const EXP = new Uint8Array(512);
24248
+ const LOG = new Uint8Array(256);
24249
+ {
24250
+ let x = 1;
24251
+ for (let i = 0; i < 255; i++) {
24252
+ EXP[i] = x;
24253
+ LOG[x] = i;
24254
+ x <<= 1;
24255
+ if (x & 0x100)
24256
+ x ^= 0x11d;
24257
+ }
24258
+ for (let i = 255; i < 512; i++)
24259
+ EXP[i] = EXP[i - 255];
24260
+ }
24261
+ const mul = (a, b) => (a && b ? EXP[LOG[a] + LOG[b]] : 0);
24262
+ /** The generator polynomial for `degree` error-correction codewords. */
24263
+ function generator(degree) {
24264
+ let poly = [1];
24265
+ for (let i = 0; i < degree; i++) {
24266
+ const next = new Array(poly.length + 1).fill(0);
24267
+ for (let j = 0; j < poly.length; j++) {
24268
+ // Descending coefficients: x * poly keeps the index, α^i * poly shifts.
24269
+ next[j] ^= poly[j];
24270
+ next[j + 1] ^= mul(poly[j], EXP[i]);
24271
+ }
24272
+ poly = next;
24273
+ }
24274
+ return poly;
24275
+ }
24276
+ /** Reed–Solomon parity for one block. */
24277
+ function remainder(data, degree) {
24278
+ const gen = generator(degree);
24279
+ const out = new Array(degree).fill(0);
24280
+ for (const byte of data) {
24281
+ const factor = byte ^ out[0];
24282
+ out.shift();
24283
+ out.push(0);
24284
+ for (let i = 0; i < degree; i++)
24285
+ out[i] ^= mul(gen[i + 1], factor);
24286
+ }
24287
+ return out;
24288
+ }
24289
+ /** The smallest version (1–10) that holds `byteLength` bytes at `ecc`. */
24290
+ function chooseVersion(byteLength, ecc) {
24291
+ for (let version = 1; version <= 10; version++) {
24292
+ const [ecPerBlock, g1, g2] = ECC_TABLE[ecc][version - 1];
24293
+ const dataCodewords = TOTAL_CODEWORDS[version - 1] - ecPerBlock * (g1 + g2);
24294
+ // 4 bits of mode + 8 or 16 bits of length, rounded up to whole codewords.
24295
+ const headerBits = 4 + (version < 10 ? 8 : 16);
24296
+ if (dataCodewords * 8 >= headerBits + byteLength * 8)
24297
+ return version;
24298
+ }
24299
+ throw new Error('strct-qr: the value is too long (versions 1–10, byte mode)');
24300
+ }
24301
+ /** The data codewords: mode, length, the bytes, a terminator, then padding. */
24302
+ function dataCodewords(bytes, version, ecc) {
24303
+ const [ecPerBlock, g1, g2] = ECC_TABLE[ecc][version - 1];
24304
+ const capacity = TOTAL_CODEWORDS[version - 1] - ecPerBlock * (g1 + g2);
24305
+ const bits = [];
24306
+ const push = (value, length) => {
24307
+ for (let i = length - 1; i >= 0; i--)
24308
+ bits.push((value >> i) & 1);
24309
+ };
24310
+ push(0b0100, 4); // byte mode
24311
+ push(bytes.length, version < 10 ? 8 : 16);
24312
+ for (const b of bytes)
24313
+ push(b, 8);
24314
+ push(0, Math.min(4, capacity * 8 - bits.length)); // terminator
24315
+ while (bits.length % 8)
24316
+ bits.push(0);
24317
+ const words = [];
24318
+ for (let i = 0; i < bits.length; i += 8) {
24319
+ words.push(bits.slice(i, i + 8).reduce((acc, bit) => (acc << 1) | bit, 0));
24320
+ }
24321
+ // Alternating pad bytes, as the spec prescribes.
24322
+ for (let i = 0; words.length < capacity; i++)
24323
+ words.push(i % 2 === 0 ? 0xec : 0x11);
24324
+ return words;
24325
+ }
24326
+ /** Interleave the blocks' data and parity, as the spec lays them out. */
24327
+ function interleave(words, version, ecc) {
24328
+ const [ecPerBlock, g1, g2] = ECC_TABLE[ecc][version - 1];
24329
+ const blocks = g1 + g2;
24330
+ const shortLen = Math.floor(words.length / blocks);
24331
+ const data = [];
24332
+ const parity = [];
24333
+ let at = 0;
24334
+ for (let i = 0; i < blocks; i++) {
24335
+ const len = i < g1 ? shortLen : shortLen + 1;
24336
+ const block = words.slice(at, at + len);
24337
+ at += len;
24338
+ data.push(block);
24339
+ parity.push(remainder(block, ecPerBlock));
24340
+ }
24341
+ const out = [];
24342
+ for (let i = 0; i < shortLen + 1; i++) {
24343
+ for (const block of data)
24344
+ if (i < block.length)
24345
+ out.push(block[i]);
24346
+ }
24347
+ for (let i = 0; i < ecPerBlock; i++)
24348
+ for (const block of parity)
24349
+ out.push(block[i]);
24350
+ return out;
24351
+ }
24352
+ var Cell;
24353
+ (function (Cell) {
24354
+ Cell[Cell["Empty"] = -1] = "Empty";
24355
+ Cell[Cell["Light"] = 0] = "Light";
24356
+ Cell[Cell["Dark"] = 1] = "Dark";
24357
+ })(Cell || (Cell = {}));
24358
+ /** Draw the finder, timing and alignment patterns, and reserve the format area. */
24359
+ function functionPatterns(size, version) {
24360
+ const m = Array.from({ length: size }, () => new Array(size).fill(Cell.Empty));
24361
+ const finder = (r0, c0) => {
24362
+ for (let dr = -1; dr <= 7; dr++) {
24363
+ for (let dc = -1; dc <= 7; dc++) {
24364
+ const r = r0 + dr;
24365
+ const c = c0 + dc;
24366
+ if (r < 0 || r >= size || c < 0 || c >= size)
24367
+ continue;
24368
+ const inner = Math.max(Math.abs(dr - 3), Math.abs(dc - 3));
24369
+ m[r][c] = inner === 2 || inner > 3 ? Cell.Light : Cell.Dark;
24370
+ }
24371
+ }
24372
+ };
24373
+ finder(0, 0);
24374
+ finder(0, size - 7);
24375
+ finder(size - 7, 0);
24376
+ for (let i = 8; i < size - 8; i++) {
24377
+ m[6][i] = m[i][6] = i % 2 === 0 ? Cell.Dark : Cell.Light;
24378
+ }
24379
+ const centres = ALIGNMENT[version - 1];
24380
+ for (const r of centres) {
24381
+ for (const c of centres) {
24382
+ // Not over a finder pattern.
24383
+ if ((r === 6 && c === 6) || (r === 6 && c === size - 7) || (r === size - 7 && c === 6))
24384
+ continue;
24385
+ for (let dr = -2; dr <= 2; dr++) {
24386
+ for (let dc = -2; dc <= 2; dc++) {
24387
+ m[r + dr][c + dc] = Math.max(Math.abs(dr), Math.abs(dc)) === 1 ? Cell.Light : Cell.Dark;
24388
+ }
24389
+ }
24390
+ }
24391
+ }
24392
+ // The dark module, always set.
24393
+ m[size - 8][8] = Cell.Dark;
24394
+ // Reserve the format areas so the data does not land there.
24395
+ for (let i = 0; i < 9; i++) {
24396
+ if (m[8][i] === Cell.Empty)
24397
+ m[8][i] = Cell.Light;
24398
+ if (m[i][8] === Cell.Empty)
24399
+ m[i][8] = Cell.Light;
24400
+ }
24401
+ for (let i = 0; i < 8; i++) {
24402
+ if (m[8][size - 1 - i] === Cell.Empty)
24403
+ m[8][size - 1 - i] = Cell.Light;
24404
+ if (m[size - 1 - i][8] === Cell.Empty)
24405
+ m[size - 1 - i][8] = Cell.Light;
24406
+ }
24407
+ return m;
24408
+ }
24409
+ const MASKS = [
24410
+ (r, c) => (r + c) % 2 === 0,
24411
+ (r) => r % 2 === 0,
24412
+ (_r, c) => c % 3 === 0,
24413
+ (r, c) => (r + c) % 3 === 0,
24414
+ (r, c) => (Math.floor(r / 2) + Math.floor(c / 3)) % 2 === 0,
24415
+ (r, c) => ((r * c) % 2) + ((r * c) % 3) === 0,
24416
+ (r, c) => (((r * c) % 2) + ((r * c) % 3)) % 2 === 0,
24417
+ (r, c) => (((r + c) % 2) + ((r * c) % 3)) % 2 === 0,
24418
+ ];
24419
+ /** The spec's penalty score — lower is better. */
24420
+ function penalty(m) {
24421
+ const size = m.length;
24422
+ let score = 0;
24423
+ const run = (line) => {
24424
+ let last = line[0];
24425
+ let length = 1;
24426
+ for (let i = 1; i < line.length; i++) {
24427
+ if (line[i] === last)
24428
+ length++;
24429
+ else {
24430
+ if (length >= 5)
24431
+ score += 3 + (length - 5);
24432
+ last = line[i];
24433
+ length = 1;
24434
+ }
24435
+ }
24436
+ if (length >= 5)
24437
+ score += 3 + (length - 5);
24438
+ };
24439
+ for (let i = 0; i < size; i++) {
24440
+ run(m[i]);
24441
+ run(m.map((row) => row[i]));
24442
+ }
24443
+ for (let r = 0; r < size - 1; r++) {
24444
+ for (let c = 0; c < size - 1; c++) {
24445
+ const v = m[r][c];
24446
+ if (v === m[r][c + 1] && v === m[r + 1][c] && v === m[r + 1][c + 1])
24447
+ score += 3;
24448
+ }
24449
+ }
24450
+ const pattern = [1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0];
24451
+ const hasPattern = (line, at) => pattern.every((bit, i) => line[at + i] === bit);
24452
+ for (let i = 0; i < size; i++) {
24453
+ const row = m[i];
24454
+ const col = m.map((r) => r[i]);
24455
+ for (let j = 0; j + 11 <= size; j++) {
24456
+ if (hasPattern(row, j))
24457
+ score += 40;
24458
+ if (hasPattern(col, j))
24459
+ score += 40;
24460
+ const rRev = [...row].reverse();
24461
+ const cRev = [...col].reverse();
24462
+ if (hasPattern(rRev, j))
24463
+ score += 40;
24464
+ if (hasPattern(cRev, j))
24465
+ score += 40;
24466
+ }
24467
+ }
24468
+ const dark = m.flat().filter((v) => v === Cell.Dark).length;
24469
+ const ratio = (dark * 100) / (size * size);
24470
+ score += Math.floor(Math.abs(ratio - 50) / 5) * 10;
24471
+ return score;
24472
+ }
24473
+ /**
24474
+ * Encode `value` as a QR matrix of booleans (true = dark module), without the
24475
+ * quiet zone — the renderer adds that.
24476
+ */
24477
+ function strctQrMatrix(value, ecc = 'M') {
24478
+ const bytes = new TextEncoder().encode(value);
24479
+ const version = chooseVersion(bytes.length, ecc);
24480
+ const size = version * 4 + 17;
24481
+ const codewords = interleave(dataCodewords(bytes, version, ecc), version, ecc);
24482
+ const base = functionPatterns(size, version);
24483
+ const reserved = base.map((row) => row.map((v) => v !== Cell.Empty));
24484
+ // Lay the data in the zig-zag, skipping the vertical timing column.
24485
+ const bits = [];
24486
+ for (const word of codewords)
24487
+ for (let i = 7; i >= 0; i--)
24488
+ bits.push((word >> i) & 1);
24489
+ let bit = 0;
24490
+ const matrix = base.map((row) => [...row]);
24491
+ for (let right = size - 1; right >= 1; right -= 2) {
24492
+ if (right === 6)
24493
+ right = 5;
24494
+ for (let vert = 0; vert < size; vert++) {
24495
+ for (let j = 0; j < 2; j++) {
24496
+ const c = right - j;
24497
+ const upward = ((right + 1) & 2) === 0;
24498
+ const r = upward ? size - 1 - vert : vert;
24499
+ if (reserved[r][c])
24500
+ continue;
24501
+ matrix[r][c] = bit < bits.length ? bits[bit++] : Cell.Light;
24502
+ }
24503
+ }
24504
+ }
24505
+ // Pick the mask with the lowest penalty, then write the format bits.
24506
+ let best = null;
24507
+ let bestMask = 0;
24508
+ let bestScore = Infinity;
24509
+ for (let mask = 0; mask < 8; mask++) {
24510
+ const candidate = matrix.map((row, r) => row.map((v, c) => (reserved[r][c] ? v : v ^ (MASKS[mask](r, c) ? 1 : 0))));
24511
+ writeFormat(candidate, ecc, mask);
24512
+ const score = penalty(candidate);
24513
+ if (score < bestScore) {
24514
+ bestScore = score;
24515
+ best = candidate;
24516
+ bestMask = mask;
24517
+ }
24518
+ }
24519
+ void bestMask;
24520
+ return best.map((row) => row.map((v) => v === Cell.Dark));
24521
+ }
24522
+ /** The 15 format bits, written in both of their places. */
24523
+ function writeFormat(m, ecc, mask) {
24524
+ const size = m.length;
24525
+ const bits = FORMAT_BITS[ecc][mask];
24526
+ const at = (i) => (bits >> i) & 1;
24527
+ for (let i = 0; i <= 5; i++)
24528
+ m[8][i] = at(i);
24529
+ m[8][7] = at(6);
24530
+ m[8][8] = at(7);
24531
+ m[7][8] = at(8);
24532
+ for (let i = 9; i <= 14; i++)
24533
+ m[14 - i][8] = at(i);
24534
+ for (let i = 0; i <= 6; i++)
24535
+ m[size - 1 - i][8] = at(i);
24536
+ for (let i = 7; i <= 14; i++)
24537
+ m[8][size - 15 + i] = at(i);
24538
+ m[size - 8][8] = 1; // the dark module, which is not a format bit
24539
+ }
24540
+ /** Internals, exported for the vector tests only. */
24541
+ const __qrInternals = { dataCodewords, remainder };
24542
+
24543
+ /**
24544
+ * A QR code the library draws, with the quiet zone and contrast a scanner needs
24545
+ * in every theme — not an image the consumer frames on white by hand.
24546
+ *
24547
+ * <strct-qr [value]="otpauthUri" label="Scan with your authenticator app" />
24548
+ *
24549
+ * **Dark modules on a light quiet zone, in every scheme.** That is a deliberate
24550
+ * exception to "tokens only": a themed QR code is an unscannable one. Show the
24551
+ * secret as text beside it, so enrolment works without a camera.
24552
+ */
24553
+ class StrctQr {
24554
+ /** What the code carries — an `otpauth://` URI, a URL, any text. */
24555
+ value = input.required(/* @ts-ignore */
24556
+ ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
24557
+ /** Rendered size in px (the code is square). */
24558
+ size = input(176, /* @ts-ignore */
24559
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
24560
+ /** The code's accessible name. */
24561
+ label = input('QR code', /* @ts-ignore */
24562
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
24563
+ /** How much of the code a scanner may lose and still read it. */
24564
+ errorCorrection = input('M', /* @ts-ignore */
24565
+ ...(ngDevMode ? [{ debugName: "errorCorrection" }] : /* istanbul ignore next */ []));
24566
+ /** The quiet zone, in modules. Four is what the spec asks for. */
24567
+ quietZone = input(4, /* @ts-ignore */
24568
+ ...(ngDevMode ? [{ debugName: "quietZone" }] : /* istanbul ignore next */ []));
24569
+ matrix = computed(() => strctQrMatrix(this.value(), this.errorCorrection()), /* @ts-ignore */
24570
+ ...(ngDevMode ? [{ debugName: "matrix" }] : /* istanbul ignore next */ []));
24571
+ /** The whole drawing's span in modules: the code plus its quiet zone. */
24572
+ span = computed(() => this.matrix().length + this.quietZone() * 2, /* @ts-ignore */
24573
+ ...(ngDevMode ? [{ debugName: "span" }] : /* istanbul ignore next */ []));
24574
+ viewBox = computed(() => `0 0 ${this.span()} ${this.span()}`, /* @ts-ignore */
24575
+ ...(ngDevMode ? [{ debugName: "viewBox" }] : /* istanbul ignore next */ []));
24576
+ /** Dark modules merged into horizontal runs — far fewer rects to paint. */
24577
+ rows = computed(() => {
24578
+ const m = this.matrix();
24579
+ const pad = this.quietZone();
24580
+ return m.map((row, y) => {
24581
+ const runs = [];
24582
+ let start = -1;
24583
+ for (let x = 0; x <= row.length; x++) {
24584
+ if (row[x]) {
24585
+ if (start < 0)
24586
+ start = x;
24587
+ }
24588
+ else if (start >= 0) {
24589
+ runs.push({ x: start + pad, width: x - start });
24590
+ start = -1;
24591
+ }
24592
+ }
24593
+ return { y: y + pad, runs };
24594
+ });
24595
+ }, /* @ts-ignore */
24596
+ ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
24597
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctQr, deps: [], target: i0.ɵɵFactoryTarget.Component });
24598
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.4", type: StrctQr, isStandalone: true, selector: "strct-qr", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, errorCorrection: { classPropertyName: "errorCorrection", publicName: "errorCorrection", isSignal: true, isRequired: false, transformFunction: null }, quietZone: { classPropertyName: "quietZone", publicName: "quietZone", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "strct-qr" }, ngImport: i0, template: `
24599
+ <svg
24600
+ class="strct-qr__svg"
24601
+ [attr.viewBox]="viewBox()"
24602
+ [attr.width]="size()"
24603
+ [attr.height]="size()"
24604
+ role="img"
24605
+ [attr.aria-label]="label()"
24606
+ shape-rendering="crispEdges"
24607
+ >
24608
+ <rect class="strct-qr__quiet" [attr.width]="span()" [attr.height]="span()" />
24609
+ @for (row of rows(); track row.y) {
24610
+ @for (run of row.runs; track run.x) {
24611
+ <rect
24612
+ class="strct-qr__module"
24613
+ [attr.x]="run.x"
24614
+ [attr.y]="row.y"
24615
+ [attr.width]="run.width"
24616
+ height="1"
24617
+ />
24618
+ }
24619
+ }
24620
+ </svg>
24621
+ `, isInline: true, styles: [".strct-qr{display:inline-flex;line-height:0}.strct-qr__svg{border-radius:var(--radius-md)}.strct-qr__quiet{fill:#fff}.strct-qr__module{fill:#000}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
24622
+ }
24623
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImport: i0, type: StrctQr, decorators: [{
24624
+ type: Component,
24625
+ args: [{ selector: 'strct-qr', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: `
24626
+ <svg
24627
+ class="strct-qr__svg"
24628
+ [attr.viewBox]="viewBox()"
24629
+ [attr.width]="size()"
24630
+ [attr.height]="size()"
24631
+ role="img"
24632
+ [attr.aria-label]="label()"
24633
+ shape-rendering="crispEdges"
24634
+ >
24635
+ <rect class="strct-qr__quiet" [attr.width]="span()" [attr.height]="span()" />
24636
+ @for (row of rows(); track row.y) {
24637
+ @for (run of row.runs; track run.x) {
24638
+ <rect
24639
+ class="strct-qr__module"
24640
+ [attr.x]="run.x"
24641
+ [attr.y]="row.y"
24642
+ [attr.width]="run.width"
24643
+ height="1"
24644
+ />
24645
+ }
24646
+ }
24647
+ </svg>
24648
+ `, host: { class: 'strct-qr' }, styles: [".strct-qr{display:inline-flex;line-height:0}.strct-qr__svg{border-radius:var(--radius-md)}.strct-qr__quiet{fill:#fff}.strct-qr__module{fill:#000}\n"] }]
24649
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], errorCorrection: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorCorrection", required: false }] }], quietZone: [{ type: i0.Input, args: [{ isSignal: true, alias: "quietZone", required: false }] }] } });
24650
+
23737
24651
  /**
23738
24652
  * A value change, as one phrase: "v10.27 → v10.28". The old value is muted, the
23739
24653
  * arrow is the library's, the new one is emphasised — and assistive tech hears
@@ -25650,5 +26564,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.4", ngImpor
25650
26564
  * Generated bundle index. Do not edit.
25651
26565
  */
25652
26566
 
25653
- export { FOCUSABLE_SELECTOR, STRCT_DP_DOW, STRCT_DP_DOW_FULL, STRCT_DP_MONTHS, STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_NOTIFICATION_HISTORY_LIMIT, STRCT_PALETTES, STRCT_RAW_ICONS, STRCT_TIME_RANGE_PRESETS, STRCT_WIZARD_DEFAULTS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAnnouncer, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctBytesPipe, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCardHeaderActions, StrctCardHeaderMeta, StrctCardHeaderNote, StrctCascadeColumn, StrctCascadeHost, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChange, StrctChart, StrctCheckbox, StrctChips, StrctCode, StrctCodeInline, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctConfirmOutlet, StrctConfirmService, StrctContextMenu, StrctContextMenuTrigger, StrctControlDescription, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatagridActionBarCaption, StrctDatepicker, StrctDatetimePicker, StrctDesc, StrctDescriptionList, StrctDiff, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctDropdownItemAction, StrctDropdownTrigger, StrctDurationPipe, StrctEmptyState, StrctField, StrctFieldGroup, StrctFieldHint, StrctFieldPrefix, StrctFieldSuffix, StrctFile, StrctFilterBar, StrctFlow, StrctFlowNodeTemplate, StrctFooter, StrctGauge, StrctHeader, StrctHeatmap, StrctHero, StrctHotkeysHelp, StrctHotkeysService, StrctIcon, StrctInlineEdit, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLegend, StrctList, StrctListItem, StrctListItemDescription, StrctListItemLeading, StrctListItemMeta, StrctListItemTrailing, StrctLiveIndicator, StrctLogViewer, StrctLogin, StrctMediaFrame, StrctMenuPanel, StrctMenuService, StrctMenubar, StrctMetricTile, StrctModal, StrctModalContent, StrctNav, StrctNavItem, StrctNotificationCenter, StrctNumber, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPageHeaderTitleMeta, StrctPagination, StrctPassword, StrctPopover, StrctPopoverTrigger, StrctProgress, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRatePipe, StrctRating, StrctReorder, StrctReorderGroup, StrctReorderHandle, StrctReorderItem, StrctResizeHandle, StrctRowDetailDef, StrctSearchbox, StrctSectionHeader, StrctSectionHeaderActions, StrctSectionHeaderMeta, StrctSectionMenu, StrctSegmented, StrctSelect, StrctShell, StrctShellService, StrctSiPipe, StrctSignpost, StrctSignpostTrigger, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctSplitButton, StrctSplitter, StrctStack, StrctStackItem, StrctStatusDot, StrctStep, StrctStepAction, StrctSteps, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctTagLeading, StrctThemeService, StrctThemeSwitcher, StrctTimeRangePicker, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctToolbar, StrctToolbarCaption, StrctToolbarSpacer, StrctTooltip, StrctTour, StrctTransfer, StrctTree, StrctTreeNode, StrctTreeSelect, StrctVerticalNav, StrctWatermark, StrctWindow, StrctWindowDock, StrctWindowService, StrctWizard, StrctWizardAside, focusFirstIn, keepTabInside, lockBodyScroll, parseAnsi, provideStrctWizardDefaults, registerStrctIcon, restoreFocus, saveFocusedElement, strctComputeDiff, strctDpPad, strctFormatBytes, strctFormatDuration, strctFormatRate, strctFormatSi, strctValidationIcon, strctValidationTone, unlockBodyScroll };
26567
+ export { FOCUSABLE_SELECTOR, STRCT_DP_DOW, STRCT_DP_DOW_FULL, STRCT_DP_MONTHS, STRCT_ICONS, STRCT_ICON_GROUPS, STRCT_ICON_NAMES, STRCT_MASKS, STRCT_NOTIFICATION_HISTORY_LIMIT, STRCT_PALETTES, STRCT_RAW_ICONS, STRCT_TIME_RANGE_PRESETS, STRCT_WIZARD_DEFAULTS, StrctAccordion, StrctAccordionPanel, StrctAlert, StrctAnnouncer, StrctAvatar, StrctBadge, StrctBreadcrumb, StrctBreadcrumbItem, StrctButton, StrctButtonGroup, StrctBytesPipe, StrctCard, StrctCardBlock, StrctCardFooter, StrctCardHeader, StrctCardHeaderActions, StrctCardHeaderMeta, StrctCardHeaderNote, StrctCascadeColumn, StrctCascadeHost, StrctCascadeSelect, StrctCellDef, StrctCellStatus, StrctChange, StrctChart, StrctChatAttachment, StrctChatComposer, StrctChatMessage, StrctChatThread, StrctCheckbox, StrctChips, StrctCode, StrctCodeInline, StrctColorPicker, StrctCombobox, StrctCommandPalette, StrctConfirmOutlet, StrctConfirmService, StrctContextMenu, StrctContextMenuTrigger, StrctControlDescription, StrctCopy, StrctDatagrid, StrctDatagridActionBar, StrctDatagridActionBarCaption, StrctDatepicker, StrctDatetimePicker, StrctDesc, StrctDescriptionList, StrctDiff, StrctDivider, StrctDonut, StrctDrawer, StrctDrawerFooter, StrctDropdown, StrctDropdownDivider, StrctDropdownItem, StrctDropdownItemAction, StrctDropdownTrigger, StrctDurationPipe, StrctEmptyState, StrctField, StrctFieldGroup, StrctFieldHint, StrctFieldPrefix, StrctFieldSuffix, StrctFile, StrctFilterBar, StrctFlow, StrctFlowNodeTemplate, StrctFooter, StrctGauge, StrctHeader, StrctHeatmap, StrctHero, StrctHotkeysHelp, StrctHotkeysService, StrctIcon, StrctInlineEdit, StrctInput, StrctInputMask, StrctInputOtp, StrctKbd, StrctKnob, StrctLegend, StrctList, StrctListItem, StrctListItemDescription, StrctListItemLeading, StrctListItemMeta, StrctListItemTrailing, StrctLiveIndicator, StrctLogViewer, StrctLogin, StrctMediaFrame, StrctMenuPanel, StrctMenuService, StrctMenubar, StrctMetricTile, StrctModal, StrctModalContent, StrctNav, StrctNavItem, StrctNotificationCenter, StrctNumber, StrctOverlay, StrctPageHeader, StrctPageHeaderActions, StrctPageHeaderCrumbs, StrctPageHeaderTitleMeta, StrctPagination, StrctPassword, StrctPopover, StrctPopoverTrigger, StrctProgress, StrctQr, StrctRadio, StrctRadioGroup, StrctRail, StrctRange, StrctRatePipe, StrctRating, StrctReorder, StrctReorderGroup, StrctReorderHandle, StrctReorderItem, StrctResizeHandle, StrctRowDetailDef, StrctSearchbox, StrctSectionHeader, StrctSectionHeaderActions, StrctSectionHeaderMeta, StrctSectionMenu, StrctSegmented, StrctSelect, StrctShell, StrctShellService, StrctSiPipe, StrctSignpost, StrctSignpostTrigger, StrctSkeleton, StrctSparkline, StrctSpeedDial, StrctSpinner, StrctSplitButton, StrctSplitter, StrctStack, StrctStackItem, StrctStatusDot, StrctStep, StrctStepAction, StrctSteps, StrctSubmenu, StrctTab, StrctTable, StrctTabs, StrctTag, StrctTagLeading, StrctThemeService, StrctThemeSwitcher, StrctTimeRangePicker, StrctTimeline, StrctTimelineItem, StrctToastOutlet, StrctToastService, StrctToggle, StrctToolbar, StrctToolbarCaption, StrctToolbarSpacer, StrctTooltip, StrctTour, StrctTransfer, StrctTree, StrctTreeNode, StrctTreeSelect, StrctVerticalNav, StrctWatermark, StrctWindow, StrctWindowDock, StrctWindowService, StrctWizard, StrctWizardAside, __qrInternals, focusFirstIn, keepTabInside, lockBodyScroll, parseAnsi, provideStrctWizardDefaults, registerStrctIcon, restoreFocus, saveFocusedElement, strctComputeDiff, strctDpPad, strctFormatBytes, strctFormatDuration, strctFormatRate, strctFormatSi, strctQrMatrix, strctValidationIcon, strctValidationTone, unlockBodyScroll };
25654
26568
  //# sourceMappingURL=akcelik-strct.mjs.map