@lifecorner/angular-ui-kit 3.6.1 → 3.8.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.
Files changed (48) hide show
  1. package/README.md +1 -0
  2. package/fesm2022/lifecorner-angular-ui-kit-character-svg.mjs +91 -0
  3. package/fesm2022/lifecorner-angular-ui-kit-character-svg.mjs.map +1 -0
  4. package/fesm2022/lifecorner-angular-ui-kit-character.mjs +138 -0
  5. package/fesm2022/lifecorner-angular-ui-kit-character.mjs.map +1 -0
  6. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs +40 -5
  7. package/fesm2022/lifecorner-angular-ui-kit-code-block.mjs.map +1 -1
  8. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs +2 -0
  9. package/fesm2022/lifecorner-angular-ui-kit-document-viewer.mjs.map +1 -1
  10. package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs +2 -0
  11. package/fesm2022/lifecorner-angular-ui-kit-markdown.mjs.map +1 -1
  12. package/fesm2022/lifecorner-angular-ui-kit.mjs +2 -0
  13. package/package.json +12 -1
  14. package/src/assets/README.md +4 -0
  15. package/src/assets/character/all-done-dark.svg +1 -0
  16. package/src/assets/character/all-done-light.svg +1 -0
  17. package/src/assets/character/calm-dark.svg +1 -0
  18. package/src/assets/character/calm-light.svg +1 -0
  19. package/src/assets/character/comforts-dark.svg +1 -0
  20. package/src/assets/character/comforts-light.svg +1 -0
  21. package/src/assets/character/connection-paused-dark.svg +1 -0
  22. package/src/assets/character/connection-paused-light.svg +1 -0
  23. package/src/assets/character/connects-dark.svg +1 -0
  24. package/src/assets/character/connects-light.svg +1 -0
  25. package/src/assets/character/discovered-dark.svg +1 -0
  26. package/src/assets/character/discovered-light.svg +1 -0
  27. package/src/assets/character/happy-dark.svg +1 -0
  28. package/src/assets/character/happy-light.svg +1 -0
  29. package/src/assets/character/hello-dark.svg +1 -0
  30. package/src/assets/character/hello-light.svg +1 -0
  31. package/src/assets/character/in-his-corner-dark.svg +1 -0
  32. package/src/assets/character/in-his-corner-light.svg +1 -0
  33. package/src/assets/character/resting-dark.svg +1 -0
  34. package/src/assets/character/resting-light.svg +1 -0
  35. package/src/assets/character/see-you-dark.svg +1 -0
  36. package/src/assets/character/see-you-light.svg +1 -0
  37. package/src/assets/character/shows-the-way-dark.svg +1 -0
  38. package/src/assets/character/shows-the-way-light.svg +1 -0
  39. package/src/assets/character/thinking-dark.svg +1 -0
  40. package/src/assets/character/thinking-light.svg +1 -0
  41. package/src/assets/character/welcomes-member-dark.svg +1 -0
  42. package/src/assets/character/welcomes-member-light.svg +1 -0
  43. package/src/styles/_horizon.scss +52 -0
  44. package/src/styles/horizon.figure.spec.ts +79 -0
  45. package/types/lifecorner-angular-ui-kit-character-svg.d.ts +44 -0
  46. package/types/lifecorner-angular-ui-kit-character.d.ts +104 -0
  47. package/types/lifecorner-angular-ui-kit-code-block.d.ts +27 -0
  48. package/types/lifecorner-angular-ui-kit.d.ts +2 -0
@@ -298,6 +298,8 @@ var CodeBlockComponent = class CodeBlockComponent {
298
298
  showLineNumbers = input(true, ...ngDevMode ? [{ debugName: "showLineNumbers" }] : /* istanbul ignore next */ []);
299
299
  showCopy = input(true, ...ngDevMode ? [{ debugName: "showCopy" }] : /* istanbul ignore next */ []);
300
300
  showHeader = input(true, ...ngDevMode ? [{ debugName: "showHeader" }] : /* istanbul ignore next */ []);
301
+ regionLabel = input(void 0, ...ngDevMode ? [{ debugName: "regionLabel" }] : /* istanbul ignore next */ []);
302
+ wrap = input(false, ...ngDevMode ? [{ debugName: "wrap" }] : /* istanbul ignore next */ []);
301
303
  copyLabel = input("Copy", ...ngDevMode ? [{ debugName: "copyLabel" }] : /* istanbul ignore next */ []);
302
304
  copyAriaLabel = input("Copy code", ...ngDevMode ? [{ debugName: "copyAriaLabel" }] : /* istanbul ignore next */ []);
303
305
  copiedLabel = input("Copied", ...ngDevMode ? [{ debugName: "copiedLabel" }] : /* istanbul ignore next */ []);
@@ -310,8 +312,11 @@ var CodeBlockComponent = class CodeBlockComponent {
310
312
  headerLabel = computed(() => {
311
313
  return this.filename() || this.language();
312
314
  }, ...ngDevMode ? [{ debugName: "headerLabel" }] : /* istanbul ignore next */ []);
315
+ regionName = computed(() => this.regionLabel() || this.headerLabel(), ...ngDevMode ? [{ debugName: "regionName" }] : /* istanbul ignore next */ []);
313
316
  codeClasses = computed(() => {
314
- return ["code-block", `code-block--${this.language()}`].join(" ");
317
+ const classes = ["code-block", `code-block--${this.language()}`];
318
+ if (this.wrap()) classes.push("code-block--wrap");
319
+ return classes.join(" ");
315
320
  }, ...ngDevMode ? [{ debugName: "codeClasses" }] : /* istanbul ignore next */ []);
316
321
  async copyToClipboard() {
317
322
  try {
@@ -377,6 +382,20 @@ var CodeBlockComponent = class CodeBlockComponent {
377
382
  isRequired: false,
378
383
  transformFunction: null
379
384
  },
385
+ regionLabel: {
386
+ classPropertyName: "regionLabel",
387
+ publicName: "regionLabel",
388
+ isSignal: true,
389
+ isRequired: false,
390
+ transformFunction: null
391
+ },
392
+ wrap: {
393
+ classPropertyName: "wrap",
394
+ publicName: "wrap",
395
+ isSignal: true,
396
+ isRequired: false,
397
+ transformFunction: null
398
+ },
380
399
  copyLabel: {
381
400
  classPropertyName: "copyLabel",
382
401
  publicName: "copyLabel",
@@ -407,8 +426,8 @@ var CodeBlockComponent = class CodeBlockComponent {
407
426
  }
408
427
  },
409
428
  ngImport: i0,
410
- template: "<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div class=\"code-block__body\">\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n",
411
- styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:none;color:var(--lc-code-block-muted-strong);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 40%,transparent)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}\n"],
429
+ template: "<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n",
430
+ styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:none;color:var(--lc-code-block-muted-strong);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 40%,transparent)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__body:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-4px}.code-block__body:focus-visible{border-radius:0 0 calc(var(--border-radius-md) - 1px) calc(var(--border-radius-md) - 1px)}.code-block__body:first-child:focus-visible{border-radius:calc(var(--border-radius-md) - 1px)}@media(forced-colors:active){.code-block__body:focus-visible{outline-color:Highlight}}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0;overflow:visible}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}.code-block--wrap .code-block__line-content{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}\n"],
412
431
  dependencies: [{
413
432
  kind: "component",
414
433
  type: IconComponent,
@@ -440,8 +459,8 @@ i0.ɵɵngDeclareClassMetadata({
440
459
  imports: [IconComponent],
441
460
  viewProviders: [ɵprovideKitIcons([ICON_CHECK])],
442
461
  changeDetection: ChangeDetectionStrategy.OnPush,
443
- template: "<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div class=\"code-block__body\">\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n",
444
- styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:none;color:var(--lc-code-block-muted-strong);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 40%,transparent)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}\n"]
462
+ template: "<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n",
463
+ styles: [".code-block{--lc-code-block-bg: #0c1722;--lc-code-block-ink: #eef4f6;--lc-code-block-muted: #93a4ad;--lc-code-block-muted-strong: #c4d2d9;--lc-code-block-border: #24343f;position:relative;border:1px solid var(--lc-code-block-border);border-radius:var(--border-radius-md);overflow:hidden;font-family:var(--typography-font-family-mono, \"SF Mono\", \"Fira Code\", \"Fira Mono\", monospace);font-size:var(--typography-font-size-sm, .875rem);line-height:1.1;background-color:var(--lc-code-block-bg);color:var(--lc-code-block-ink)}.code-block__header{display:flex;justify-content:space-between;align-items:center;min-height:2.75rem;padding:0 .25rem 0 1rem;background-color:#00000040;border-bottom:1px solid var(--lc-code-block-border)}.code-block__language{font-size:var(--typography-font-size-xs, .75rem);color:var(--lc-code-block-muted);text-transform:lowercase;-webkit-user-select:none;user-select:none}.code-block__copy{display:inline-flex;align-items:center;justify-content:center;gap:.25rem;min-height:2.75rem;min-width:2.75rem;padding:0 .75rem;border:none;border-radius:var(--border-radius-sm);background:transparent;color:var(--lc-code-block-muted);font-size:var(--typography-font-size-xs, .75rem);font-family:inherit;cursor:pointer;transition:color .15s ease,background-color .15s ease}.code-block__copy:hover{color:var(--lc-code-block-muted-strong);background-color:#ffffff0f}.code-block__copy:focus-visible{outline:none;color:var(--lc-code-block-muted-strong);box-shadow:0 0 0 3px color-mix(in srgb,var(--color-primary-500) 40%,transparent)}.code-block__live{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.code-block__body{overflow-x:auto}.code-block__body:focus-visible{outline:2px solid var(--color-primary-500);outline-offset:-4px}.code-block__body:focus-visible{border-radius:0 0 calc(var(--border-radius-md) - 1px) calc(var(--border-radius-md) - 1px)}.code-block__body:first-child:focus-visible{border-radius:calc(var(--border-radius-md) - 1px)}@media(forced-colors:active){.code-block__body:focus-visible{outline-color:Highlight}}.code-block__pre{margin:0;padding:1rem;background:transparent;color:inherit;border-radius:0;overflow:visible}.code-block__code{display:block}.code-block__line{display:flex}.code-block__line-number{display:inline-block;min-width:2.5rem;padding-right:1rem;text-align:right;color:var(--lc-code-block-muted);-webkit-user-select:none;user-select:none;flex-shrink:0}.code-block__line-content{flex:1;white-space:pre}.code-block__line-content ::ng-deep .token-keyword{color:#c678dd}.code-block__line-content ::ng-deep .token-string{color:#98c379}.code-block__line-content ::ng-deep .token-number{color:#d19a66}.code-block__line-content ::ng-deep .token-comment{color:var(--lc-code-block-muted);font-style:italic}.code-block__line-content ::ng-deep .token-type{color:#e5c07b}.code-block__line-content ::ng-deep .token-constant{color:#d19a66}.code-block__line-content ::ng-deep .token-decorator{color:#61afef}.code-block__line-content ::ng-deep .token-tag{color:#e06c75}.code-block__line-content ::ng-deep .token-attr{color:#d19a66}.code-block__line-content ::ng-deep .token-selector{color:#e06c75}.code-block__line-content ::ng-deep .token-property{color:#61afef}.code-block__line-content ::ng-deep .token-unit{color:#d19a66}.code-block__line-content ::ng-deep .token-variable{color:#e06c75}.code-block__line-content ::ng-deep .token-builtin{color:#61afef}.code-block__line-content ::ng-deep .token-flag{color:#56b6c2}.code-block--wrap .code-block__line-content{white-space:pre-wrap;overflow-wrap:anywhere;min-width:0}\n"]
445
464
  }]
446
465
  }],
447
466
  propDecorators: {
@@ -493,6 +512,22 @@ i0.ɵɵngDeclareClassMetadata({
493
512
  required: false
494
513
  }]
495
514
  }],
515
+ regionLabel: [{
516
+ type: i0.Input,
517
+ args: [{
518
+ isSignal: true,
519
+ alias: "regionLabel",
520
+ required: false
521
+ }]
522
+ }],
523
+ wrap: [{
524
+ type: i0.Input,
525
+ args: [{
526
+ isSignal: true,
527
+ alias: "wrap",
528
+ required: false
529
+ }]
530
+ }],
496
531
  copyLabel: [{
497
532
  type: i0.Input,
498
533
  args: [{
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-code-block.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.ts","../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n signal,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHECK } from '@lifecorner/angular-ui-kit/icons';\n\nexport type CodeBlockLanguage =\n | 'typescript'\n | 'javascript'\n | 'html'\n | 'css'\n | 'scss'\n | 'json'\n | 'bash'\n | 'python'\n | 'java'\n | 'text';\n\ninterface TokenRule {\n pattern: RegExp;\n className: string;\n}\n\nconst SHARED_RULES: TokenRule[] = [\n // Strings (double/single/backtick)\n { pattern: /`(?:[^`\\\\]|\\\\.)*`/g, className: 'token-string' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"/g, className: 'token-string' },\n { pattern: /'(?:[^'\\\\]|\\\\.)*'/g, className: 'token-string' },\n // Numbers\n { pattern: /\\b\\d+(?:\\.\\d+)?\\b/g, className: 'token-number' },\n];\n\nconst LANGUAGE_RULES: Record<string, TokenRule[]> = {\n typescript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|interface|type|enum|extends|implements|new|this|super|async|await|yield|of|in|as|is|readonly|public|private|protected|static|abstract|override|get|set|constructor|declare|namespace|module|require)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:string|number|boolean|void|null|undefined|never|any|unknown|object|symbol|bigint)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n { pattern: /(?<=\\b(?:class|interface|type|enum|extends|implements)\\s+)\\w+/g, className: 'token-type' },\n ],\n javascript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|extends|new|this|super|async|await|yield|of|in|typeof|instanceof|delete|throw|try|catch|finally|switch|case|default|break|continue|do)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n ],\n // The tokenizer reads the raw source (3.6.1): `<` and `>` instead of the\n // `&lt;`/`&gt;` the escaped text had. Up to 3.6.0 an html comment rule ran on\n // that escaped text and never matched; it is gone, so comments stay plain.\n html: [\n ...SHARED_RULES,\n { pattern: /<\\/?[\\w-]+/g, className: 'token-tag' },\n { pattern: /\\/?>/g, className: 'token-tag' },\n { pattern: /\\b[\\w-]+(?==)/g, className: 'token-attr' },\n ],\n css: [\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n { pattern: /\\b(?:px|rem|em|%|vh|vw|s|ms|deg|fr)\\b/g, className: 'token-unit' },\n { pattern: /[\\w-]+(?=\\s*:)/g, className: 'token-property' },\n ],\n scss: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w-]+/g, className: 'token-variable' },\n { pattern: /@(?:use|forward|import|mixin|include|extend|if|else|each|for|while|function|return)\\b/g, className: 'token-keyword' },\n { pattern: /&/g, className: 'token-selector' },\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n ],\n json: [\n ...SHARED_RULES,\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"(?=\\s*:)/g, className: 'token-property' },\n ],\n bash: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w{][\\w}]*/g, className: 'token-variable' },\n { pattern: /\\b(?:if|then|else|elif|fi|for|do|done|while|until|case|esac|function|in|select|time|coproc)\\b/g, className: 'token-keyword' },\n { pattern: /(?:^|\\s)(?:npm|npx|yarn|pnpm|git|cd|ls|mkdir|rm|cp|mv|echo|cat|grep|find|sed|awk|curl|wget|docker|node|python|pip|brew|apt|sudo)\\b/g, className: 'token-builtin' },\n { pattern: /--?[\\w-]+/g, className: 'token-flag' },\n ],\n python: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n { pattern: /\"\"\"[\\s\\S]*?\"\"\"/g, className: 'token-string' },\n { pattern: /'''[\\s\\S]*?'''/g, className: 'token-string' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:def|class|import|from|as|return|if|elif|else|for|while|break|continue|pass|raise|try|except|finally|with|yield|lambda|assert|del|global|nonlocal|async|await|in|not|and|or|is)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:True|False|None)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:str|int|float|bool|list|dict|tuple|set|bytes|type|object|Exception|ValueError|TypeError|KeyError|IndexError|AttributeError|RuntimeError|StopIteration)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:print|len|range|enumerate|zip|map|filter|sorted|reversed|isinstance|issubclass|hasattr|getattr|setattr|super|property|staticmethod|classmethod|open|input)\\b/g, className: 'token-builtin' },\n { pattern: /\\bself\\b/g, className: 'token-variable' },\n ],\n java: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:abstract|assert|break|case|catch|class|continue|default|do|else|enum|extends|final|finally|for|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|volatile|while|var|record|sealed|permits|yield)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:void|int|long|short|byte|float|double|char|boolean|String|Integer|Long|Double|Float|Boolean|Character|Object|Class|List|Map|Set|ArrayList|HashMap|HashSet|Optional|Stream|Collectors)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:System|Math|Arrays|Collections|Objects|Thread|Runnable)\\b/g, className: 'token-builtin' },\n ],\n};\n\n/** A run of source text; `className` is one `token-*` class, or `null` for plain text. */\ninterface CodeSegment {\n readonly text: string;\n readonly className: string | null;\n}\n\n/** One rendered line: its segments in order. */\ntype CodeLine = readonly CodeSegment[];\n\n/**\n * What an empty line renders: U+00A0, the glyph the `&nbsp;` of 3.6.0\n * produced, so the line keeps its height and a selection its text.\n */\nconst EMPTY_LINE: CodeLine = [{ text: String.fromCharCode(0xa0), className: null }];\n\n/** Matches of the language's rules on the raw source, sorted, without overlaps. */\nfunction tokenize(code: string, language: string): { start: number; end: number; className: string }[] {\n const rules = LANGUAGE_RULES[language];\n if (!rules) return [];\n\n // Collect all matches with positions\n const tokens: { start: number; end: number; className: string }[] = [];\n\n for (const rule of rules) {\n const regex = new RegExp(rule.pattern.source, rule.pattern.flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(code)) !== null) {\n tokens.push({\n start: match.index,\n end: match.index + match[0].length,\n className: rule.className,\n });\n }\n }\n\n // Sort by start position, longer matches first for same position\n tokens.sort((a, b) => a.start - b.start || b.end - a.end);\n\n // Remove overlapping tokens (keep first/longest)\n const filtered: typeof tokens = [];\n let lastEnd = 0;\n for (const token of tokens) {\n if (token.start >= lastEnd) {\n filtered.push(token);\n lastEnd = token.end;\n }\n }\n return filtered;\n}\n\n/**\n * The highlighted source as lines of text segments, rendered by the template\n * as text nodes and `<span class=\"token-…\">` elements: no markup string, so\n * nothing for Trusted Types to block and nothing in the source is parsed.\n *\n * A token that runs across a line break keeps its class on the part before\n * the first break only; the later parts are plain, as the HTML parser closed\n * the open span at each line end up to 3.6.0.\n */\nfunction highlightLines(code: string, language: string): CodeLine[] {\n const lines: CodeSegment[][] = [[]];\n const append = (text: string, className: string | null) => {\n text.split('\\n').forEach((part, i) => {\n if (i > 0) lines.push([]);\n if (!part) return;\n const line = lines[lines.length - 1];\n const cls = i === 0 ? className : null;\n const last = line[line.length - 1];\n if (cls === null && last?.className === null) {\n line[line.length - 1] = { text: last.text + part, className: null };\n } else {\n line.push({ text: part, className: cls });\n }\n });\n };\n\n let cursor = 0;\n for (const token of tokenize(code, language)) {\n append(code.slice(cursor, token.start), null);\n append(code.slice(token.start, token.end), token.className);\n cursor = token.end;\n }\n append(code.slice(cursor), null);\n\n return lines.map(line => (line.length ? line : EMPTY_LINE));\n}\n\n@Component({\n selector: 'lc-code-block',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK])],\n templateUrl: './code-block.component.html',\n styleUrls: ['./code-block.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n/**\n * Code block component for displaying syntax-highlighted source code.\n *\n * Features:\n * - Syntax highlighting for 10+ languages (TypeScript, Python, Java, etc.)\n * - Optional line numbers\n * - Copy-to-clipboard button\n * - Optional header with filename and language label\n * - Dark-mode compatible color scheme\n *\n * @example\n * ```html\n * <lc-code-block [code]=\"source\" language=\"typescript\" [showLineNumbers]=\"true\" />\n * ```\n */\nexport class CodeBlockComponent {\n /** Code content to display */\n code = input.required<string>();\n\n /** Language label */\n language = input<CodeBlockLanguage>('text');\n\n /** Optional filename to display */\n filename = input<string>();\n\n /** Whether to show line numbers */\n showLineNumbers = input<boolean>(true);\n\n /** Whether to show copy button */\n showCopy = input<boolean>(true);\n\n /** Whether the code block has a header bar */\n showHeader = input<boolean>(true);\n\n /** Visible label of the copy button. */\n copyLabel = input('Copy');\n\n /** Accessible name of the copy button. */\n copyAriaLabel = input('Copy code');\n\n /** Label of the copy button after copying (also its accessible name). */\n copiedLabel = input('Copied');\n\n /** Announced to screen readers after copying. */\n copiedAnnouncement = input('Copied to clipboard');\n\n protected copied = signal(false);\n\n protected highlightedLines = computed<CodeLine[]>(() =>\n highlightLines(this.code(), this.language())\n );\n\n protected lineNumbers = computed(() => {\n return this.code().split('\\n').map((_, i) => i + 1);\n });\n\n protected headerLabel = computed(() => {\n return this.filename() || this.language();\n });\n\n protected codeClasses = computed(() => {\n return ['code-block', `code-block--${this.language()}`].join(' ');\n });\n\n protected async copyToClipboard(): Promise<void> {\n try {\n await navigator.clipboard.writeText(this.code());\n this.copied.set(true);\n setTimeout(() => this.copied.set(false), 2000);\n } catch {\n // Fallback: just ignore if clipboard API not available\n }\n }\n}\n","<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div class=\"code-block__body\">\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-code-block.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.ts","../../../../libs/angular-ui-kit/src/lib/code-block/code-block.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n input,\n computed,\n signal,\n} from '@angular/core';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_CHECK } from '@lifecorner/angular-ui-kit/icons';\n\nexport type CodeBlockLanguage =\n | 'typescript'\n | 'javascript'\n | 'html'\n | 'css'\n | 'scss'\n | 'json'\n | 'bash'\n | 'python'\n | 'java'\n | 'text';\n\ninterface TokenRule {\n pattern: RegExp;\n className: string;\n}\n\nconst SHARED_RULES: TokenRule[] = [\n // Strings (double/single/backtick)\n { pattern: /`(?:[^`\\\\]|\\\\.)*`/g, className: 'token-string' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"/g, className: 'token-string' },\n { pattern: /'(?:[^'\\\\]|\\\\.)*'/g, className: 'token-string' },\n // Numbers\n { pattern: /\\b\\d+(?:\\.\\d+)?\\b/g, className: 'token-number' },\n];\n\nconst LANGUAGE_RULES: Record<string, TokenRule[]> = {\n typescript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|interface|type|enum|extends|implements|new|this|super|async|await|yield|of|in|as|is|readonly|public|private|protected|static|abstract|override|get|set|constructor|declare|namespace|module|require)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:string|number|boolean|void|null|undefined|never|any|unknown|object|symbol|bigint)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n { pattern: /(?<=\\b(?:class|interface|type|enum|extends|implements)\\s+)\\w+/g, className: 'token-type' },\n ],\n javascript: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\b(?:import|export|from|const|let|var|function|return|if|else|for|while|class|extends|new|this|super|async|await|yield|of|in|typeof|instanceof|delete|throw|try|catch|finally|switch|case|default|break|continue|do)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null|undefined|NaN|Infinity)\\b/g, className: 'token-constant' },\n ],\n // The tokenizer reads the raw source (3.6.1): `<` and `>` instead of the\n // `&lt;`/`&gt;` the escaped text had. Up to 3.6.0 an html comment rule ran on\n // that escaped text and never matched; it is gone, so comments stay plain.\n html: [\n ...SHARED_RULES,\n { pattern: /<\\/?[\\w-]+/g, className: 'token-tag' },\n { pattern: /\\/?>/g, className: 'token-tag' },\n { pattern: /\\b[\\w-]+(?==)/g, className: 'token-attr' },\n ],\n css: [\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n { pattern: /\\b(?:px|rem|em|%|vh|vw|s|ms|deg|fr)\\b/g, className: 'token-unit' },\n { pattern: /[\\w-]+(?=\\s*:)/g, className: 'token-property' },\n ],\n scss: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w-]+/g, className: 'token-variable' },\n { pattern: /@(?:use|forward|import|mixin|include|extend|if|else|each|for|while|function|return)\\b/g, className: 'token-keyword' },\n { pattern: /&/g, className: 'token-selector' },\n { pattern: /[.#][\\w-]+/g, className: 'token-selector' },\n ],\n json: [\n ...SHARED_RULES,\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\"(?:[^\"\\\\]|\\\\.)*\"(?=\\s*:)/g, className: 'token-property' },\n ],\n bash: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /\\$[\\w{][\\w}]*/g, className: 'token-variable' },\n { pattern: /\\b(?:if|then|else|elif|fi|for|do|done|while|until|case|esac|function|in|select|time|coproc)\\b/g, className: 'token-keyword' },\n { pattern: /(?:^|\\s)(?:npm|npx|yarn|pnpm|git|cd|ls|mkdir|rm|cp|mv|echo|cat|grep|find|sed|awk|curl|wget|docker|node|python|pip|brew|apt|sudo)\\b/g, className: 'token-builtin' },\n { pattern: /--?[\\w-]+/g, className: 'token-flag' },\n ],\n python: [\n { pattern: /#.*$/gm, className: 'token-comment' },\n { pattern: /\"\"\"[\\s\\S]*?\"\"\"/g, className: 'token-string' },\n { pattern: /'''[\\s\\S]*?'''/g, className: 'token-string' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:def|class|import|from|as|return|if|elif|else|for|while|break|continue|pass|raise|try|except|finally|with|yield|lambda|assert|del|global|nonlocal|async|await|in|not|and|or|is)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:True|False|None)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:str|int|float|bool|list|dict|tuple|set|bytes|type|object|Exception|ValueError|TypeError|KeyError|IndexError|AttributeError|RuntimeError|StopIteration)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:print|len|range|enumerate|zip|map|filter|sorted|reversed|isinstance|issubclass|hasattr|getattr|setattr|super|property|staticmethod|classmethod|open|input)\\b/g, className: 'token-builtin' },\n { pattern: /\\bself\\b/g, className: 'token-variable' },\n ],\n java: [\n { pattern: /\\/\\/.*$/gm, className: 'token-comment' },\n { pattern: /\\/\\*[\\s\\S]*?\\*\\//g, className: 'token-comment' },\n ...SHARED_RULES,\n { pattern: /@\\w+/g, className: 'token-decorator' },\n { pattern: /\\b(?:abstract|assert|break|case|catch|class|continue|default|do|else|enum|extends|final|finally|for|if|implements|import|instanceof|interface|native|new|package|private|protected|public|return|static|strictfp|super|switch|synchronized|this|throw|throws|transient|try|volatile|while|var|record|sealed|permits|yield)\\b/g, className: 'token-keyword' },\n { pattern: /\\b(?:true|false|null)\\b/g, className: 'token-constant' },\n { pattern: /\\b(?:void|int|long|short|byte|float|double|char|boolean|String|Integer|Long|Double|Float|Boolean|Character|Object|Class|List|Map|Set|ArrayList|HashMap|HashSet|Optional|Stream|Collectors)\\b/g, className: 'token-type' },\n { pattern: /\\b(?:System|Math|Arrays|Collections|Objects|Thread|Runnable)\\b/g, className: 'token-builtin' },\n ],\n};\n\n/** A run of source text; `className` is one `token-*` class, or `null` for plain text. */\ninterface CodeSegment {\n readonly text: string;\n readonly className: string | null;\n}\n\n/** One rendered line: its segments in order. */\ntype CodeLine = readonly CodeSegment[];\n\n/**\n * What an empty line renders: U+00A0, the glyph the `&nbsp;` of 3.6.0\n * produced, so the line keeps its height and a selection its text.\n */\nconst EMPTY_LINE: CodeLine = [{ text: String.fromCharCode(0xa0), className: null }];\n\n/** Matches of the language's rules on the raw source, sorted, without overlaps. */\nfunction tokenize(code: string, language: string): { start: number; end: number; className: string }[] {\n const rules = LANGUAGE_RULES[language];\n if (!rules) return [];\n\n // Collect all matches with positions\n const tokens: { start: number; end: number; className: string }[] = [];\n\n for (const rule of rules) {\n const regex = new RegExp(rule.pattern.source, rule.pattern.flags);\n let match: RegExpExecArray | null;\n while ((match = regex.exec(code)) !== null) {\n tokens.push({\n start: match.index,\n end: match.index + match[0].length,\n className: rule.className,\n });\n }\n }\n\n // Sort by start position, longer matches first for same position\n tokens.sort((a, b) => a.start - b.start || b.end - a.end);\n\n // Remove overlapping tokens (keep first/longest)\n const filtered: typeof tokens = [];\n let lastEnd = 0;\n for (const token of tokens) {\n if (token.start >= lastEnd) {\n filtered.push(token);\n lastEnd = token.end;\n }\n }\n return filtered;\n}\n\n/**\n * The highlighted source as lines of text segments, rendered by the template\n * as text nodes and `<span class=\"token-…\">` elements: no markup string, so\n * nothing for Trusted Types to block and nothing in the source is parsed.\n *\n * A token that runs across a line break keeps its class on the part before\n * the first break only; the later parts are plain, as the HTML parser closed\n * the open span at each line end up to 3.6.0.\n */\nfunction highlightLines(code: string, language: string): CodeLine[] {\n const lines: CodeSegment[][] = [[]];\n const append = (text: string, className: string | null) => {\n text.split('\\n').forEach((part, i) => {\n if (i > 0) lines.push([]);\n if (!part) return;\n const line = lines[lines.length - 1];\n const cls = i === 0 ? className : null;\n const last = line[line.length - 1];\n if (cls === null && last?.className === null) {\n line[line.length - 1] = { text: last.text + part, className: null };\n } else {\n line.push({ text: part, className: cls });\n }\n });\n };\n\n let cursor = 0;\n for (const token of tokenize(code, language)) {\n append(code.slice(cursor, token.start), null);\n append(code.slice(token.start, token.end), token.className);\n cursor = token.end;\n }\n append(code.slice(cursor), null);\n\n return lines.map(line => (line.length ? line : EMPTY_LINE));\n}\n\n@Component({\n selector: 'lc-code-block',\n standalone: true,\n imports: [IconComponent],\n viewProviders: [ɵprovideKitIcons([ICON_CHECK])],\n templateUrl: './code-block.component.html',\n styleUrls: ['./code-block.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\n/**\n * Code block component for displaying syntax-highlighted source code.\n *\n * Features:\n * - Syntax highlighting for 10+ languages (TypeScript, Python, Java, etc.)\n * - Optional line numbers\n * - Copy-to-clipboard button\n * - Optional header with filename and language label\n * - Dark-mode compatible color scheme\n * - Keyboard: the body is a focusable, named region that scrolls with the\n * arrow keys; `wrap` wraps instead\n *\n * @example\n * ```html\n * <lc-code-block [code]=\"source\" language=\"typescript\" [showLineNumbers]=\"true\" />\n * ```\n */\nexport class CodeBlockComponent {\n /** Code content to display */\n code = input.required<string>();\n\n /** Language label */\n language = input<CodeBlockLanguage>('text');\n\n /** Optional filename to display */\n filename = input<string>();\n\n /** Whether to show line numbers */\n showLineNumbers = input<boolean>(true);\n\n /** Whether to show copy button */\n showCopy = input<boolean>(true);\n\n /** Whether the code block has a header bar */\n showHeader = input<boolean>(true);\n\n /**\n * Accessible name of the scrollable code region (`role=\"region\"`). When it\n * is unset or empty, the `filename` is used, then the `language`. Set a\n * short, localised name that is unique on the page, such as \"Befehl für\n * Claude Code\". Not used when `wrap` is set.\n * @default undefined\n */\n regionLabel = input<string | undefined>(undefined);\n\n /**\n * Wraps long lines (`white-space: pre-wrap`, breaking anywhere if needed)\n * instead of scrolling sideways. A wrapped block doesn't scroll, so its body\n * is neither a tab stop nor a region.\n * @default false\n */\n wrap = input<boolean>(false);\n\n /** Visible label of the copy button. */\n copyLabel = input('Copy');\n\n /** Accessible name of the copy button. */\n copyAriaLabel = input('Copy code');\n\n /** Label of the copy button after copying (also its accessible name). */\n copiedLabel = input('Copied');\n\n /** Announced to screen readers after copying. */\n copiedAnnouncement = input('Copied to clipboard');\n\n protected copied = signal(false);\n\n protected highlightedLines = computed<CodeLine[]>(() =>\n highlightLines(this.code(), this.language())\n );\n\n protected lineNumbers = computed(() => {\n return this.code().split('\\n').map((_, i) => i + 1);\n });\n\n protected headerLabel = computed(() => {\n return this.filename() || this.language();\n });\n\n /** The region's name: `regionLabel`, then `filename`, then `language` (`''` falls through). */\n protected regionName = computed(() => this.regionLabel() || this.headerLabel());\n\n protected codeClasses = computed(() => {\n const classes = ['code-block', `code-block--${this.language()}`];\n if (this.wrap()) classes.push('code-block--wrap');\n return classes.join(' ');\n });\n\n protected async copyToClipboard(): Promise<void> {\n try {\n await navigator.clipboard.writeText(this.code());\n this.copied.set(true);\n setTimeout(() => this.copied.set(false), 2000);\n } catch {\n // Fallback: just ignore if clipboard API not available\n }\n }\n}\n","<div [class]=\"codeClasses()\">\n @if (showHeader()) {\n <div class=\"code-block__header\">\n <span class=\"code-block__language\">{{ headerLabel() }}</span>\n @if (showCopy()) {\n <button\n type=\"button\"\n class=\"code-block__copy\"\n [attr.aria-label]=\"copied() ? copiedLabel() : copyAriaLabel()\"\n (click)=\"copyToClipboard()\"\n >\n @if (copied()) {\n <lc-icon name=\"check\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copiedLabel() }}</span>\n } @else {\n <lc-icon name=\"clipboard-document\" size=\"sm\" [decorative]=\"true\" />\n <span>{{ copyLabel() }}</span>\n }\n </button>\n <span class=\"code-block__live\" role=\"status\" aria-live=\"polite\">{{ copied() ? copiedAnnouncement() : '' }}</span>\n }\n </div>\n }\n <div\n class=\"code-block__body\"\n [attr.tabindex]=\"wrap() ? null : 0\"\n [attr.role]=\"wrap() ? null : 'region'\"\n [attr.aria-label]=\"wrap() ? null : regionName()\"\n >\n <pre class=\"code-block__pre\"><code class=\"code-block__code\">@for (line of highlightedLines(); track $index; let i = $index) {<span class=\"code-block__line\">@if (showLineNumbers()) {<span class=\"code-block__line-number\">{{ lineNumbers()[i] }}</span>}<span class=\"code-block__line-content\">@for (segment of line; track $index) {@if (segment.className) {<span [attr.class]=\"segment.className\">{{ segment.text }}</span>}@else{{{ segment.text }}}}</span></span>\n}</code></pre>\n </div>\n</div>\n"],"mappings":"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"}
@@ -487,6 +487,8 @@ var DocumentViewerComponent = class DocumentViewerComponent {
487
487
  "showLineNumbers",
488
488
  "showCopy",
489
489
  "showHeader",
490
+ "regionLabel",
491
+ "wrap",
490
492
  "copyLabel",
491
493
  "copyAriaLabel",
492
494
  "copiedLabel",
@@ -1 +1 @@
1
- {"version":3,"file":"lifecorner-angular-ui-kit-document-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n OnDestroy,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT, ICON_PLUS } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { CodeBlockComponent, CodeBlockLanguage } from '@lifecorner/angular-ui-kit/code-block';\nimport { MarkdownComponent } from '@lifecorner/angular-ui-kit/markdown';\nimport { isSafeResourceUrl } from '@lifecorner/angular-ui-kit/internal/url';\n\nexport type DocumentType = 'pdf' | 'markdown' | 'image' | 'text' | 'code' | 'auto';\n\nconst EXTENSION_TYPE_MAP: Record<string, DocumentType> = {\n pdf: 'pdf',\n md: 'markdown',\n markdown: 'markdown',\n png: 'image',\n jpg: 'image',\n jpeg: 'image',\n gif: 'image',\n webp: 'image',\n svg: 'image',\n bmp: 'image',\n ts: 'code',\n js: 'code',\n tsx: 'code',\n jsx: 'code',\n py: 'code',\n java: 'code',\n html: 'code',\n css: 'code',\n scss: 'code',\n json: 'code',\n sh: 'code',\n bash: 'code',\n txt: 'text',\n log: 'text',\n csv: 'text',\n};\n\nconst EXTENSION_LANGUAGE_MAP: Record<string, CodeBlockLanguage> = {\n ts: 'typescript',\n tsx: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n py: 'python',\n java: 'java',\n html: 'html',\n css: 'css',\n scss: 'scss',\n json: 'json',\n sh: 'bash',\n bash: 'bash',\n};\n\nconst TYPE_ICONS: Record<string, string> = {\n pdf: 'document-text',\n markdown: 'document-text',\n image: 'photo',\n text: 'document',\n code: 'code-bracket',\n unknown: 'document',\n};\n\n/**\n * Document viewer component for previewing various file types.\n *\n * Features:\n * - Auto-detects file type from URL extension or explicit type input\n * - PDF rendering via browser-native iframe viewer\n * - Markdown rendered through `lc-markdown` (escaped and sanitised — a fetched\n * `.md` file cannot inject markup)\n * - Image display with zoom controls (25% – 500%)\n * - Code display using the built-in code block component\n * - Plain text display\n * - Toolbar with filename, type badge, zoom, download, and fullscreen\n * - Loading and error states\n * - Dark/light theme support\n *\n * `src` is only ever embedded (iframe) or downloaded when it is a http(s),\n * blob: or relative URL — or a `data:application/pdf` — so a `javascript:` URL\n * handed in as a document can neither run in the frame nor be \"downloaded\".\n *\n * @example\n * ```html\n * <lc-document-viewer src=\"https://example.com/report.pdf\" />\n * <lc-document-viewer [content]=\"markdownString\" type=\"markdown\" filename=\"README.md\" />\n * <lc-document-viewer src=\"/assets/diagram.png\" />\n * ```\n */\n@Component({\n selector: 'lc-document-viewer',\n standalone: true,\n imports: [IconComponent, SpinnerComponent, CodeBlockComponent, MarkdownComponent],\n viewProviders: [ɵprovideKitIcons([ICON_PLUS, ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT])],\n templateUrl: './document-viewer.component.html',\n styleUrls: ['./document-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': '\"doc-viewer\"',\n '[style.height]': 'height()',\n },\n})\nexport class DocumentViewerComponent implements OnDestroy {\n /** URL of the document to display (for PDF, images, or remote files) */\n readonly src = input<string>('');\n\n /** Direct content string (for markdown, text, or code — takes precedence over src for content-based types) */\n readonly content = input<string>('');\n\n /** File type — set to 'auto' to detect from URL extension */\n readonly type = input<DocumentType>('auto');\n\n /** Display name shown in the toolbar */\n readonly filename = input<string>('');\n\n /** Code language for syntax highlighting (when type is 'code') */\n readonly language = input<CodeBlockLanguage>('text');\n\n /** Whether to display the toolbar */\n readonly showToolbar = input(true);\n\n /** Whether to show the download button in the toolbar */\n readonly showDownload = input(true);\n\n /** Height of the viewer container */\n readonly height = input('500px');\n\n /** Accessible name of the zoom-out button. */\n readonly zoomOutAriaLabel = input('Zoom out');\n\n /** Accessible name of the zoom-in button. */\n readonly zoomInAriaLabel = input('Zoom in');\n\n /** Accessible name of the reset-zoom button. */\n readonly resetZoomAriaLabel = input('Reset zoom');\n\n /** Accessible name of the toolbar download button. */\n readonly downloadAriaLabel = input('Download');\n\n /** Accessible name of the fullscreen button. */\n readonly fullscreenAriaLabel = input('Fullscreen');\n\n /** Accessible name of the fullscreen button while in fullscreen. */\n readonly exitFullscreenAriaLabel = input('Exit fullscreen');\n\n /** Shown while the document loads. */\n readonly loadingLabel = input('Loading document…');\n\n /** Title shown for file types without a preview. */\n readonly previewUnavailableTitle = input('Preview not available');\n\n /** Message shown for file types without a preview. */\n readonly previewUnavailableMessage = input('This file type cannot be previewed.');\n\n /** Label of the download button shown instead of a preview. */\n readonly downloadFileLabel = input('Download file');\n\n /** Toolbar name when neither `filename` nor a file name in `src` is available. */\n readonly untitledLabel = input('Document');\n\n /** Type badge for Markdown documents. */\n readonly markdownTypeLabel = input('Markdown');\n\n /** Type badge for images. */\n readonly imageTypeLabel = input('Image');\n\n /** Type badge for plain text. */\n readonly textTypeLabel = input('Text');\n\n /** Type badge for other files. */\n readonly fileTypeLabel = input('File');\n\n /** Prefix of the error shown when fetching the document fails. */\n readonly loadErrorLabel = input('Failed to load document:');\n\n /** Error shown when `src` is not an allowed URL (e.g. `javascript:`). */\n readonly blockedUrlMessage = input('This document URL cannot be displayed');\n\n // ── Internal state ───────────────────────────────────────────────────────\n\n protected loading = signal(true);\n protected error = signal<string | null>(null);\n protected fetchedContent = signal<string>('');\n protected zoom = signal(100);\n protected isFullscreen = signal(false);\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly elementRef = inject(ElementRef);\n private fullscreenHandler: (() => void) | null = null;\n\n // ── Computed values ──────────────────────────────────────────────────────\n\n /** Resolved document type from input or URL extension */\n protected resolvedType = computed<DocumentType | 'unknown'>(() => {\n const t = this.type();\n if (t !== 'auto') return t;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_TYPE_MAP[ext] ? EXTENSION_TYPE_MAP[ext] : 'unknown';\n });\n\n /** Display name for the toolbar */\n protected displayName = computed(() => {\n if (this.filename()) return this.filename();\n const src = this.src();\n if (!src) return this.untitledLabel();\n try {\n const url = new URL(src, 'http://localhost');\n const segments = url.pathname.split('/');\n return segments[segments.length - 1] || this.untitledLabel();\n } catch {\n return src.split('/').pop() || this.untitledLabel();\n }\n });\n\n /** Icon name for the document type */\n protected typeIcon = computed(() => TYPE_ICONS[this.resolvedType()] || TYPE_ICONS['unknown']);\n\n /** Type label for the toolbar badge */\n protected typeLabel = computed(() => {\n const t = this.resolvedType();\n switch (t) {\n case 'pdf': return 'PDF';\n case 'markdown': return this.markdownTypeLabel();\n case 'image': return this.imageTypeLabel();\n case 'text': return this.textTypeLabel();\n case 'code': return this.resolvedLanguage().toUpperCase();\n default: return this.fileTypeLabel();\n }\n });\n\n /** Resolved code language */\n protected resolvedLanguage = computed<CodeBlockLanguage>(() => {\n const lang = this.language();\n if (lang !== 'text') return lang;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_LANGUAGE_MAP[ext] ? EXTENSION_LANGUAGE_MAP[ext] : 'text';\n });\n\n /** Whether `src` may be embedded in a frame or downloaded at all. */\n protected srcEmbeddable = computed(() => isSafeResourceUrl(this.src()));\n\n /**\n * `src` as the iframe accepts it. Bypassing the resource-URL check is what\n * lets a PDF URL into an iframe at all; it is only done for URLs that passed\n * `isSafeResourceUrl` — anything else resolves to an empty frame.\n */\n protected safeSrc = computed<SafeResourceUrl>(() => {\n const src = this.src();\n return this.sanitizer.bypassSecurityTrustResourceUrl(this.srcEmbeddable() ? src : 'about:blank');\n });\n\n /** Effective content: direct content input or fetched content */\n protected effectiveContent = computed(() => {\n return this.content() || this.fetchedContent();\n });\n\n /** Image transform style for zoom */\n protected imageTransform = computed(() => {\n const z = this.zoom() / 100;\n return `scale(${z})`;\n });\n\n /** Whether zoom controls should be shown */\n protected showZoom = computed(() => {\n const t = this.resolvedType();\n return t === 'image';\n });\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n constructor() {\n // Load whenever the document changes — not just once. A new `src` starts a\n // fresh load (and cancels the one still in flight, so a slow old response\n // can't overwrite the new document), resets any earlier error, and re-enters\n // the loading state; a `content` string short-circuits all of that.\n effect((onCleanup) => {\n const type = this.resolvedType();\n const src = this.src();\n const content = this.content();\n const controller = typeof AbortController === 'function' ? new AbortController() : null;\n onCleanup(() => controller?.abort());\n untracked(() => this.load(type, src, content, controller));\n });\n }\n\n private load(\n type: DocumentType | 'unknown',\n src: string,\n content: string,\n controller: AbortController | null,\n ): void {\n this.error.set(null);\n this.fetchedContent.set('');\n\n if (content) {\n this.loading.set(false);\n return;\n }\n\n if (!src) {\n this.loading.set(false);\n this.error.set('No source or content provided');\n return;\n }\n\n // PDF and images are embedded directly — no fetch needed. They do have to be\n // embeddable, though: a `javascript:` (or other exotic) URL is refused up\n // front instead of being handed to an iframe.\n if (type === 'pdf' || type === 'image') {\n if (!this.srcEmbeddable()) {\n this.loading.set(false);\n this.error.set(this.blockedUrlMessage());\n return;\n }\n // Not \"loading\": the frame/img element only exists once loading is off,\n // and it is its own load event that would end the state — a deadlock.\n this.loading.set(false);\n return;\n }\n\n // Fetch text-based content\n if (type === 'markdown' || type === 'text' || type === 'code') {\n this.loading.set(true);\n this.fetchContent(src, controller);\n } else {\n this.loading.set(false);\n }\n }\n\n ngOnDestroy(): void {\n if (this.fullscreenHandler) {\n document.removeEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n // ── Actions ──────────────────────────────────────────────────────────────\n\n protected zoomIn(): void {\n this.zoom.update(z => Math.min(z + 25, 500));\n }\n\n protected zoomOut(): void {\n this.zoom.update(z => Math.max(z - 25, 25));\n }\n\n protected resetZoom(): void {\n this.zoom.set(100);\n }\n\n protected download(): void {\n const src = this.src();\n // A download is a click the user never sees the target of — only URLs that\n // could be embedded are worth clicking.\n if (!src || !this.srcEmbeddable()) return;\n const a = document.createElement('a');\n a.href = src;\n a.download = this.displayName();\n a.target = '_blank';\n a.rel = 'noopener noreferrer';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n\n protected toggleFullscreen(): void {\n const el = this.elementRef.nativeElement as HTMLElement;\n if (!document.fullscreenElement) {\n el.requestFullscreen?.();\n this.isFullscreen.set(true);\n } else {\n document.exitFullscreen?.();\n this.isFullscreen.set(false);\n }\n\n if (!this.fullscreenHandler) {\n this.fullscreenHandler = () => {\n this.isFullscreen.set(!!document.fullscreenElement);\n };\n document.addEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n protected onIframeLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageError(): void {\n this.loading.set(false);\n this.error.set('Failed to load image');\n }\n\n // ── Private helpers ──────────────────────────────────────────────────────\n\n private extractExtension(url: string): string | null {\n if (!url) return null;\n try {\n const pathname = new URL(url, 'http://localhost').pathname;\n const parts = pathname.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : null;\n } catch {\n const parts = url.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase().split('?')[0] : null;\n }\n }\n\n private async fetchContent(url: string, controller: AbortController | null): Promise<void> {\n if (typeof fetch !== 'function') {\n this.loading.set(false);\n this.error.set('Failed to load document: fetch is not available');\n return;\n }\n try {\n const response = await fetch(url, controller ? { signal: controller.signal } : undefined);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n const text = await response.text();\n // A response for a document that has since been replaced is dropped: the\n // effect that replaced it aborted this controller and started its own load.\n if (controller?.signal.aborted) return;\n this.fetchedContent.set(text);\n this.loading.set(false);\n } catch (err) {\n if (controller?.signal.aborted) return;\n this.loading.set(false);\n this.error.set(`${this.loadErrorLabel()} ${err instanceof Error ? err.message : 'Unknown error'}`);\n }\n }\n}\n","<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"doc-viewer__toolbar\">\n <div class=\"doc-viewer__toolbar-left\">\n <lc-icon [name]=\"typeIcon()\" size=\"sm\" />\n <span class=\"doc-viewer__filename\">{{ displayName() }}</span>\n <span class=\"doc-viewer__badge\">{{ typeLabel() }}</span>\n </div>\n <div class=\"doc-viewer__toolbar-right\">\n @if (showZoom()) {\n <div class=\"doc-viewer__zoom\">\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomOutAriaLabel()\"\n (click)=\"zoomOut()\"\n >\n <lc-icon name=\"minus\" size=\"xs\" />\n </button>\n <span class=\"doc-viewer__zoom-value\">{{ zoom() }}%</span>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomInAriaLabel()\"\n (click)=\"zoomIn()\"\n >\n <lc-icon name=\"plus\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"resetZoomAriaLabel()\"\n (click)=\"resetZoom()\"\n >\n <lc-icon name=\"arrows-pointing-out\" size=\"xs\" />\n </button>\n </div>\n }\n @if (showDownload() && src()) {\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"downloadAriaLabel()\"\n (click)=\"download()\"\n >\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"isFullscreen() ? exitFullscreenAriaLabel() : fullscreenAriaLabel()\"\n (click)=\"toggleFullscreen()\"\n >\n <lc-icon [name]=\"isFullscreen() ? 'arrows-pointing-in' : 'arrows-pointing-out'\" size=\"sm\" />\n </button>\n </div>\n </div>\n}\n\n<!-- Content area -->\n<div class=\"doc-viewer__content\">\n @if (loading()) {\n <div class=\"doc-viewer__loading\">\n <lc-spinner size=\"lg\" />\n <span>{{ loadingLabel() }}</span>\n </div>\n }\n\n @if (error(); as errorMsg) {\n <div class=\"doc-viewer__error\">\n <lc-icon name=\"exclamation-triangle\" size=\"lg\" color=\"var(--color-error-default)\" />\n <span>{{ errorMsg }}</span>\n </div>\n }\n\n @if (!loading() && !error()) {\n @switch (resolvedType()) {\n @case ('pdf') {\n <iframe\n class=\"doc-viewer__iframe\"\n [src]=\"safeSrc()\"\n [title]=\"displayName()\"\n type=\"application/pdf\"\n (load)=\"onIframeLoad()\"\n ></iframe>\n }\n\n @case ('image') {\n <div class=\"doc-viewer__image-container\">\n <img\n class=\"doc-viewer__image\"\n [src]=\"src()\"\n [alt]=\"displayName()\"\n [style.transform]=\"imageTransform()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n </div>\n }\n\n @case ('markdown') {\n <!-- lc-markdown escapes the source and runs Angular's sanitizer — the\n one markdown renderer in the kit, so its safety work isn't duplicated\n (or missed) here. -->\n <div class=\"doc-viewer__markdown\">\n <lc-markdown [content]=\"effectiveContent()\" linkTarget=\"_blank\" />\n </div>\n }\n\n @case ('code') {\n <lc-code-block\n [code]=\"effectiveContent()\"\n [language]=\"resolvedLanguage()\"\n [filename]=\"displayName()\"\n [showLineNumbers]=\"true\"\n [showHeader]=\"false\"\n />\n }\n\n @case ('text') {\n <pre class=\"doc-viewer__text\">{{ effectiveContent() }}</pre>\n }\n\n @default {\n <div class=\"doc-viewer__unsupported\">\n <lc-icon name=\"document\" size=\"lg\" />\n <span class=\"doc-viewer__unsupported-title\">{{ previewUnavailableTitle() }}</span>\n <span class=\"doc-viewer__unsupported-msg\">{{ previewUnavailableMessage() }}</span>\n @if (src()) {\n <button type=\"button\" class=\"doc-viewer__download-btn\" (click)=\"download()\">\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n {{ downloadFileLabel() }}\n </button>\n }\n </div>\n }\n }\n }\n</div>\n"],"mappings":"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"}
1
+ {"version":3,"file":"lifecorner-angular-ui-kit-document-viewer.mjs","names":[],"sources":["../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.ts","../../../../libs/angular-ui-kit/src/lib/document-viewer/document-viewer.component.html"],"sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n ElementRef,\n OnDestroy,\n ViewEncapsulation,\n computed,\n effect,\n inject,\n input,\n signal,\n untracked,\n} from '@angular/core';\nimport { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';\nimport { IconComponent, ɵprovideKitIcons } from '@lifecorner/angular-ui-kit/icon';\nimport { ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT, ICON_PLUS } from '@lifecorner/angular-ui-kit/icons';\nimport { SpinnerComponent } from '@lifecorner/angular-ui-kit/spinner';\nimport { CodeBlockComponent, CodeBlockLanguage } from '@lifecorner/angular-ui-kit/code-block';\nimport { MarkdownComponent } from '@lifecorner/angular-ui-kit/markdown';\nimport { isSafeResourceUrl } from '@lifecorner/angular-ui-kit/internal/url';\n\nexport type DocumentType = 'pdf' | 'markdown' | 'image' | 'text' | 'code' | 'auto';\n\nconst EXTENSION_TYPE_MAP: Record<string, DocumentType> = {\n pdf: 'pdf',\n md: 'markdown',\n markdown: 'markdown',\n png: 'image',\n jpg: 'image',\n jpeg: 'image',\n gif: 'image',\n webp: 'image',\n svg: 'image',\n bmp: 'image',\n ts: 'code',\n js: 'code',\n tsx: 'code',\n jsx: 'code',\n py: 'code',\n java: 'code',\n html: 'code',\n css: 'code',\n scss: 'code',\n json: 'code',\n sh: 'code',\n bash: 'code',\n txt: 'text',\n log: 'text',\n csv: 'text',\n};\n\nconst EXTENSION_LANGUAGE_MAP: Record<string, CodeBlockLanguage> = {\n ts: 'typescript',\n tsx: 'typescript',\n js: 'javascript',\n jsx: 'javascript',\n py: 'python',\n java: 'java',\n html: 'html',\n css: 'css',\n scss: 'scss',\n json: 'json',\n sh: 'bash',\n bash: 'bash',\n};\n\nconst TYPE_ICONS: Record<string, string> = {\n pdf: 'document-text',\n markdown: 'document-text',\n image: 'photo',\n text: 'document',\n code: 'code-bracket',\n unknown: 'document',\n};\n\n/**\n * Document viewer component for previewing various file types.\n *\n * Features:\n * - Auto-detects file type from URL extension or explicit type input\n * - PDF rendering via browser-native iframe viewer\n * - Markdown rendered through `lc-markdown` (escaped and sanitised — a fetched\n * `.md` file cannot inject markup)\n * - Image display with zoom controls (25% – 500%)\n * - Code display using the built-in code block component\n * - Plain text display\n * - Toolbar with filename, type badge, zoom, download, and fullscreen\n * - Loading and error states\n * - Dark/light theme support\n *\n * `src` is only ever embedded (iframe) or downloaded when it is a http(s),\n * blob: or relative URL — or a `data:application/pdf` — so a `javascript:` URL\n * handed in as a document can neither run in the frame nor be \"downloaded\".\n *\n * @example\n * ```html\n * <lc-document-viewer src=\"https://example.com/report.pdf\" />\n * <lc-document-viewer [content]=\"markdownString\" type=\"markdown\" filename=\"README.md\" />\n * <lc-document-viewer src=\"/assets/diagram.png\" />\n * ```\n */\n@Component({\n selector: 'lc-document-viewer',\n standalone: true,\n imports: [IconComponent, SpinnerComponent, CodeBlockComponent, MarkdownComponent],\n viewProviders: [ɵprovideKitIcons([ICON_PLUS, ICON_EXCLAMATION_TRIANGLE, ICON_FILE, ICON_FILE_TEXT])],\n templateUrl: './document-viewer.component.html',\n styleUrls: ['./document-viewer.component.scss'],\n changeDetection: ChangeDetectionStrategy.OnPush,\n encapsulation: ViewEncapsulation.None,\n host: {\n '[class]': '\"doc-viewer\"',\n '[style.height]': 'height()',\n },\n})\nexport class DocumentViewerComponent implements OnDestroy {\n /** URL of the document to display (for PDF, images, or remote files) */\n readonly src = input<string>('');\n\n /** Direct content string (for markdown, text, or code — takes precedence over src for content-based types) */\n readonly content = input<string>('');\n\n /** File type — set to 'auto' to detect from URL extension */\n readonly type = input<DocumentType>('auto');\n\n /** Display name shown in the toolbar */\n readonly filename = input<string>('');\n\n /** Code language for syntax highlighting (when type is 'code') */\n readonly language = input<CodeBlockLanguage>('text');\n\n /** Whether to display the toolbar */\n readonly showToolbar = input(true);\n\n /** Whether to show the download button in the toolbar */\n readonly showDownload = input(true);\n\n /** Height of the viewer container */\n readonly height = input('500px');\n\n /** Accessible name of the zoom-out button. */\n readonly zoomOutAriaLabel = input('Zoom out');\n\n /** Accessible name of the zoom-in button. */\n readonly zoomInAriaLabel = input('Zoom in');\n\n /** Accessible name of the reset-zoom button. */\n readonly resetZoomAriaLabel = input('Reset zoom');\n\n /** Accessible name of the toolbar download button. */\n readonly downloadAriaLabel = input('Download');\n\n /** Accessible name of the fullscreen button. */\n readonly fullscreenAriaLabel = input('Fullscreen');\n\n /** Accessible name of the fullscreen button while in fullscreen. */\n readonly exitFullscreenAriaLabel = input('Exit fullscreen');\n\n /** Shown while the document loads. */\n readonly loadingLabel = input('Loading document…');\n\n /** Title shown for file types without a preview. */\n readonly previewUnavailableTitle = input('Preview not available');\n\n /** Message shown for file types without a preview. */\n readonly previewUnavailableMessage = input('This file type cannot be previewed.');\n\n /** Label of the download button shown instead of a preview. */\n readonly downloadFileLabel = input('Download file');\n\n /** Toolbar name when neither `filename` nor a file name in `src` is available. */\n readonly untitledLabel = input('Document');\n\n /** Type badge for Markdown documents. */\n readonly markdownTypeLabel = input('Markdown');\n\n /** Type badge for images. */\n readonly imageTypeLabel = input('Image');\n\n /** Type badge for plain text. */\n readonly textTypeLabel = input('Text');\n\n /** Type badge for other files. */\n readonly fileTypeLabel = input('File');\n\n /** Prefix of the error shown when fetching the document fails. */\n readonly loadErrorLabel = input('Failed to load document:');\n\n /** Error shown when `src` is not an allowed URL (e.g. `javascript:`). */\n readonly blockedUrlMessage = input('This document URL cannot be displayed');\n\n // ── Internal state ───────────────────────────────────────────────────────\n\n protected loading = signal(true);\n protected error = signal<string | null>(null);\n protected fetchedContent = signal<string>('');\n protected zoom = signal(100);\n protected isFullscreen = signal(false);\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly elementRef = inject(ElementRef);\n private fullscreenHandler: (() => void) | null = null;\n\n // ── Computed values ──────────────────────────────────────────────────────\n\n /** Resolved document type from input or URL extension */\n protected resolvedType = computed<DocumentType | 'unknown'>(() => {\n const t = this.type();\n if (t !== 'auto') return t;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_TYPE_MAP[ext] ? EXTENSION_TYPE_MAP[ext] : 'unknown';\n });\n\n /** Display name for the toolbar */\n protected displayName = computed(() => {\n if (this.filename()) return this.filename();\n const src = this.src();\n if (!src) return this.untitledLabel();\n try {\n const url = new URL(src, 'http://localhost');\n const segments = url.pathname.split('/');\n return segments[segments.length - 1] || this.untitledLabel();\n } catch {\n return src.split('/').pop() || this.untitledLabel();\n }\n });\n\n /** Icon name for the document type */\n protected typeIcon = computed(() => TYPE_ICONS[this.resolvedType()] || TYPE_ICONS['unknown']);\n\n /** Type label for the toolbar badge */\n protected typeLabel = computed(() => {\n const t = this.resolvedType();\n switch (t) {\n case 'pdf': return 'PDF';\n case 'markdown': return this.markdownTypeLabel();\n case 'image': return this.imageTypeLabel();\n case 'text': return this.textTypeLabel();\n case 'code': return this.resolvedLanguage().toUpperCase();\n default: return this.fileTypeLabel();\n }\n });\n\n /** Resolved code language */\n protected resolvedLanguage = computed<CodeBlockLanguage>(() => {\n const lang = this.language();\n if (lang !== 'text') return lang;\n const ext = this.extractExtension(this.src());\n return ext && EXTENSION_LANGUAGE_MAP[ext] ? EXTENSION_LANGUAGE_MAP[ext] : 'text';\n });\n\n /** Whether `src` may be embedded in a frame or downloaded at all. */\n protected srcEmbeddable = computed(() => isSafeResourceUrl(this.src()));\n\n /**\n * `src` as the iframe accepts it. Bypassing the resource-URL check is what\n * lets a PDF URL into an iframe at all; it is only done for URLs that passed\n * `isSafeResourceUrl` — anything else resolves to an empty frame.\n */\n protected safeSrc = computed<SafeResourceUrl>(() => {\n const src = this.src();\n return this.sanitizer.bypassSecurityTrustResourceUrl(this.srcEmbeddable() ? src : 'about:blank');\n });\n\n /** Effective content: direct content input or fetched content */\n protected effectiveContent = computed(() => {\n return this.content() || this.fetchedContent();\n });\n\n /** Image transform style for zoom */\n protected imageTransform = computed(() => {\n const z = this.zoom() / 100;\n return `scale(${z})`;\n });\n\n /** Whether zoom controls should be shown */\n protected showZoom = computed(() => {\n const t = this.resolvedType();\n return t === 'image';\n });\n\n // ── Lifecycle ────────────────────────────────────────────────────────────\n\n constructor() {\n // Load whenever the document changes — not just once. A new `src` starts a\n // fresh load (and cancels the one still in flight, so a slow old response\n // can't overwrite the new document), resets any earlier error, and re-enters\n // the loading state; a `content` string short-circuits all of that.\n effect((onCleanup) => {\n const type = this.resolvedType();\n const src = this.src();\n const content = this.content();\n const controller = typeof AbortController === 'function' ? new AbortController() : null;\n onCleanup(() => controller?.abort());\n untracked(() => this.load(type, src, content, controller));\n });\n }\n\n private load(\n type: DocumentType | 'unknown',\n src: string,\n content: string,\n controller: AbortController | null,\n ): void {\n this.error.set(null);\n this.fetchedContent.set('');\n\n if (content) {\n this.loading.set(false);\n return;\n }\n\n if (!src) {\n this.loading.set(false);\n this.error.set('No source or content provided');\n return;\n }\n\n // PDF and images are embedded directly — no fetch needed. They do have to be\n // embeddable, though: a `javascript:` (or other exotic) URL is refused up\n // front instead of being handed to an iframe.\n if (type === 'pdf' || type === 'image') {\n if (!this.srcEmbeddable()) {\n this.loading.set(false);\n this.error.set(this.blockedUrlMessage());\n return;\n }\n // Not \"loading\": the frame/img element only exists once loading is off,\n // and it is its own load event that would end the state — a deadlock.\n this.loading.set(false);\n return;\n }\n\n // Fetch text-based content\n if (type === 'markdown' || type === 'text' || type === 'code') {\n this.loading.set(true);\n this.fetchContent(src, controller);\n } else {\n this.loading.set(false);\n }\n }\n\n ngOnDestroy(): void {\n if (this.fullscreenHandler) {\n document.removeEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n // ── Actions ──────────────────────────────────────────────────────────────\n\n protected zoomIn(): void {\n this.zoom.update(z => Math.min(z + 25, 500));\n }\n\n protected zoomOut(): void {\n this.zoom.update(z => Math.max(z - 25, 25));\n }\n\n protected resetZoom(): void {\n this.zoom.set(100);\n }\n\n protected download(): void {\n const src = this.src();\n // A download is a click the user never sees the target of — only URLs that\n // could be embedded are worth clicking.\n if (!src || !this.srcEmbeddable()) return;\n const a = document.createElement('a');\n a.href = src;\n a.download = this.displayName();\n a.target = '_blank';\n a.rel = 'noopener noreferrer';\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n }\n\n protected toggleFullscreen(): void {\n const el = this.elementRef.nativeElement as HTMLElement;\n if (!document.fullscreenElement) {\n el.requestFullscreen?.();\n this.isFullscreen.set(true);\n } else {\n document.exitFullscreen?.();\n this.isFullscreen.set(false);\n }\n\n if (!this.fullscreenHandler) {\n this.fullscreenHandler = () => {\n this.isFullscreen.set(!!document.fullscreenElement);\n };\n document.addEventListener('fullscreenchange', this.fullscreenHandler);\n }\n }\n\n protected onIframeLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageLoad(): void {\n this.loading.set(false);\n }\n\n protected onImageError(): void {\n this.loading.set(false);\n this.error.set('Failed to load image');\n }\n\n // ── Private helpers ──────────────────────────────────────────────────────\n\n private extractExtension(url: string): string | null {\n if (!url) return null;\n try {\n const pathname = new URL(url, 'http://localhost').pathname;\n const parts = pathname.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase() : null;\n } catch {\n const parts = url.split('.');\n return parts.length > 1 ? parts.pop()!.toLowerCase().split('?')[0] : null;\n }\n }\n\n private async fetchContent(url: string, controller: AbortController | null): Promise<void> {\n if (typeof fetch !== 'function') {\n this.loading.set(false);\n this.error.set('Failed to load document: fetch is not available');\n return;\n }\n try {\n const response = await fetch(url, controller ? { signal: controller.signal } : undefined);\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}: ${response.statusText}`);\n }\n const text = await response.text();\n // A response for a document that has since been replaced is dropped: the\n // effect that replaced it aborted this controller and started its own load.\n if (controller?.signal.aborted) return;\n this.fetchedContent.set(text);\n this.loading.set(false);\n } catch (err) {\n if (controller?.signal.aborted) return;\n this.loading.set(false);\n this.error.set(`${this.loadErrorLabel()} ${err instanceof Error ? err.message : 'Unknown error'}`);\n }\n }\n}\n","<!-- Toolbar -->\n@if (showToolbar()) {\n <div class=\"doc-viewer__toolbar\">\n <div class=\"doc-viewer__toolbar-left\">\n <lc-icon [name]=\"typeIcon()\" size=\"sm\" />\n <span class=\"doc-viewer__filename\">{{ displayName() }}</span>\n <span class=\"doc-viewer__badge\">{{ typeLabel() }}</span>\n </div>\n <div class=\"doc-viewer__toolbar-right\">\n @if (showZoom()) {\n <div class=\"doc-viewer__zoom\">\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomOutAriaLabel()\"\n (click)=\"zoomOut()\"\n >\n <lc-icon name=\"minus\" size=\"xs\" />\n </button>\n <span class=\"doc-viewer__zoom-value\">{{ zoom() }}%</span>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"zoomInAriaLabel()\"\n (click)=\"zoomIn()\"\n >\n <lc-icon name=\"plus\" size=\"xs\" />\n </button>\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"resetZoomAriaLabel()\"\n (click)=\"resetZoom()\"\n >\n <lc-icon name=\"arrows-pointing-out\" size=\"xs\" />\n </button>\n </div>\n }\n @if (showDownload() && src()) {\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"downloadAriaLabel()\"\n (click)=\"download()\"\n >\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n </button>\n }\n <button\n type=\"button\"\n class=\"doc-viewer__btn\"\n [attr.aria-label]=\"isFullscreen() ? exitFullscreenAriaLabel() : fullscreenAriaLabel()\"\n (click)=\"toggleFullscreen()\"\n >\n <lc-icon [name]=\"isFullscreen() ? 'arrows-pointing-in' : 'arrows-pointing-out'\" size=\"sm\" />\n </button>\n </div>\n </div>\n}\n\n<!-- Content area -->\n<div class=\"doc-viewer__content\">\n @if (loading()) {\n <div class=\"doc-viewer__loading\">\n <lc-spinner size=\"lg\" />\n <span>{{ loadingLabel() }}</span>\n </div>\n }\n\n @if (error(); as errorMsg) {\n <div class=\"doc-viewer__error\">\n <lc-icon name=\"exclamation-triangle\" size=\"lg\" color=\"var(--color-error-default)\" />\n <span>{{ errorMsg }}</span>\n </div>\n }\n\n @if (!loading() && !error()) {\n @switch (resolvedType()) {\n @case ('pdf') {\n <iframe\n class=\"doc-viewer__iframe\"\n [src]=\"safeSrc()\"\n [title]=\"displayName()\"\n type=\"application/pdf\"\n (load)=\"onIframeLoad()\"\n ></iframe>\n }\n\n @case ('image') {\n <div class=\"doc-viewer__image-container\">\n <img\n class=\"doc-viewer__image\"\n [src]=\"src()\"\n [alt]=\"displayName()\"\n [style.transform]=\"imageTransform()\"\n (load)=\"onImageLoad()\"\n (error)=\"onImageError()\"\n />\n </div>\n }\n\n @case ('markdown') {\n <!-- lc-markdown escapes the source and runs Angular's sanitizer — the\n one markdown renderer in the kit, so its safety work isn't duplicated\n (or missed) here. -->\n <div class=\"doc-viewer__markdown\">\n <lc-markdown [content]=\"effectiveContent()\" linkTarget=\"_blank\" />\n </div>\n }\n\n @case ('code') {\n <lc-code-block\n [code]=\"effectiveContent()\"\n [language]=\"resolvedLanguage()\"\n [filename]=\"displayName()\"\n [showLineNumbers]=\"true\"\n [showHeader]=\"false\"\n />\n }\n\n @case ('text') {\n <pre class=\"doc-viewer__text\">{{ effectiveContent() }}</pre>\n }\n\n @default {\n <div class=\"doc-viewer__unsupported\">\n <lc-icon name=\"document\" size=\"lg\" />\n <span class=\"doc-viewer__unsupported-title\">{{ previewUnavailableTitle() }}</span>\n <span class=\"doc-viewer__unsupported-msg\">{{ previewUnavailableMessage() }}</span>\n @if (src()) {\n <button type=\"button\" class=\"doc-viewer__download-btn\" (click)=\"download()\">\n <lc-icon name=\"arrow-down-tray\" size=\"sm\" />\n {{ downloadFileLabel() }}\n </button>\n }\n </div>\n }\n }\n }\n</div>\n"],"mappings":"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"}
@@ -564,6 +564,8 @@ var MarkdownComponent = class MarkdownComponent {
564
564
  "showLineNumbers",
565
565
  "showCopy",
566
566
  "showHeader",
567
+ "regionLabel",
568
+ "wrap",
567
569
  "copyLabel",
568
570
  "copyAriaLabel",
569
571
  "copiedLabel",