@mantine-vue/code-highlight 3.1.0 → 3.3.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.
package/cjs/index.cjs CHANGED
@@ -19,15 +19,49 @@ var CodeHighlightAdapterProvider_default = /* @__PURE__ */ (0, vue.defineCompone
19
19
  setup(__props) {
20
20
  const props = __props;
21
21
  const context = (0, vue.ref)(null);
22
+ const contextLoaded = (0, vue.ref)(false);
23
+ const loadedLanguages = (0, vue.ref)([]);
24
+ const requestedLanguages = /* @__PURE__ */ new Set();
25
+ let generation = 0;
22
26
  const highlight = (0, vue.computed)(() => props.adapter.getHighlighter(context.value));
23
- (0, vue.onMounted)(() => props.adapter.loadContext?.().then((value) => {
24
- context.value = value;
25
- }));
27
+ (0, vue.watch)(() => props.adapter, async (adapter) => {
28
+ const currentGeneration = ++generation;
29
+ context.value = null;
30
+ contextLoaded.value = !adapter.loadContext;
31
+ loadedLanguages.value = [];
32
+ requestedLanguages.clear();
33
+ if (adapter.loadContext) {
34
+ const value = await adapter.loadContext();
35
+ if (currentGeneration === generation) {
36
+ context.value = value;
37
+ contextLoaded.value = true;
38
+ }
39
+ }
40
+ }, { immediate: true });
41
+ function loadLanguage(language) {
42
+ if (!contextLoaded.value || !language || !props.adapter.loadLanguage || requestedLanguages.has(language)) return;
43
+ const currentGeneration = generation;
44
+ requestedLanguages.add(language);
45
+ let result;
46
+ try {
47
+ result = props.adapter.loadLanguage(context.value, language);
48
+ } catch {
49
+ requestedLanguages.delete(language);
50
+ return;
51
+ }
52
+ result?.then(() => {
53
+ if (currentGeneration === generation && !loadedLanguages.value.includes(language)) loadedLanguages.value = [...loadedLanguages.value, language];
54
+ }, () => currentGeneration === generation && requestedLanguages.delete(language));
55
+ }
26
56
  provideCodeHighlightAdapterContext({
27
- adapter: props.adapter,
57
+ get adapter() {
58
+ return props.adapter;
59
+ },
28
60
  get highlight() {
29
61
  return highlight.value;
30
- }
62
+ },
63
+ loadLanguage,
64
+ isLanguageLoaded: (language) => !language || !props.adapter.loadLanguage || loadedLanguages.value.includes(language)
31
65
  });
32
66
  return (_ctx, _cache) => {
33
67
  return (0, vue.openBlock)(), (0, vue.createElementBlock)("div", _hoisted_1$1, [(0, vue.renderSlot)(_ctx.$slots, "default")]);
@@ -41,10 +75,24 @@ function provideCodeHighlightAdapterContext(value) {
41
75
  (0, vue.provide)(CodeHighlightContext, value);
42
76
  }
43
77
  function useHighlight() {
44
- return (0, vue.inject)(CodeHighlightContext, null)?.highlight || plainTextAdapter.getHighlighter(null);
78
+ const ctx = (0, vue.inject)(CodeHighlightContext, null);
79
+ const fallback = plainTextAdapter.getHighlighter(null);
80
+ return (input) => (ctx?.highlight || fallback)(input);
81
+ }
82
+ function useLoadLanguage() {
83
+ return (0, vue.inject)(CodeHighlightContext, null)?.loadLanguage || (() => {});
84
+ }
85
+ function useIsLanguageLoaded() {
86
+ return (0, vue.inject)(CodeHighlightContext, null)?.isLanguageLoaded || (() => true);
45
87
  }
46
88
  var CodeHighlightAdapterProvider = CodeHighlightAdapterProvider_default;
47
89
  //#endregion
90
+ //#region src/normalize-code.ts
91
+ /** Removes blank leading lines and trailing whitespace before highlighting or copying code. */
92
+ function normalizeCode(code, { withFirstLineIndentation } = {}) {
93
+ return withFirstLineIndentation ? code.replace(/^(?:[^\S\r\n]*\r?\n)+|\s+$/g, "") : code.trim();
94
+ }
95
+ //#endregion
48
96
  //#region src/CodeHighlight/CodeHighlight.context.ts
49
97
  var [provideCodeHighlightContext, useCodeHighlightContext] = (0, _mantine_vue_core.createSafeContext)("CodeHighlightProvider was not found in the component tree");
50
98
  //#endregion
@@ -163,7 +211,7 @@ var CopyCodeButton_default = /* @__PURE__ */ (0, vue.defineComponent)({
163
211
  variant: "none",
164
212
  "tooltip-label": (0, vue.unref)(clipboard).copied.value ? props.copiedLabel : props.copyLabel,
165
213
  "aria-label": (0, vue.unref)(clipboard).copied.value ? props.copiedLabel : `${props.copyLabel} code`,
166
- onClick: _cache[0] || (_cache[0] = ($event) => (0, vue.unref)(clipboard).copy(props.code.trim()))
214
+ onClick: _cache[0] || (_cache[0] = ($event) => (0, vue.unref)(clipboard).copy(props.code))
167
215
  }, {
168
216
  default: (0, vue.withCtx)(() => [(0, vue.createVNode)(CopyIcon_default, { copied: (0, vue.unref)(clipboard).copied.value }, null, 8, ["copied"])]),
169
217
  _: 1
@@ -309,7 +357,8 @@ var CodeHighlight_default = /* @__PURE__ */ (0, vue.defineComponent)({
309
357
  default: false
310
358
  },
311
359
  controls: {},
312
- codeColorScheme: {}
360
+ codeColorScheme: {},
361
+ withFirstLineIndentation: { type: Boolean }
313
362
  },
314
363
  emits: ["update:expanded", "expanded-change"],
315
364
  setup(__props, { emit: __emit }) {
@@ -342,21 +391,28 @@ var CodeHighlight_default = /* @__PURE__ */ (0, vue.defineComponent)({
342
391
  }
343
392
  });
344
393
  const highlight = useHighlight();
394
+ const loadLanguage = useLoadLanguage();
395
+ const isLanguageLoaded = useIsLanguageLoaded();
396
+ const normalizedCode = (0, vue.computed)(() => normalizeCode(props.code, { withFirstLineIndentation: props.withFirstLineIndentation }));
397
+ (0, vue.watchEffect)(() => loadLanguage(props.language));
345
398
  function resolveColorScheme(value) {
346
399
  if (value !== "auto") return value;
347
400
  return typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches ? "dark" : "light";
348
401
  }
349
- const highlighted = (0, vue.computed)(() => highlight({
350
- code: props.code.trim(),
351
- language: props.language,
352
- colorScheme: props.codeColorScheme ?? resolveColorScheme(mantine.colorScheme.value)
353
- }));
402
+ const highlighted = (0, vue.computed)(() => {
403
+ isLanguageLoaded(props.language);
404
+ return highlight({
405
+ code: normalizedCode.value,
406
+ language: props.language,
407
+ colorScheme: props.codeColorScheme ?? resolveColorScheme(mantine.colorScheme.value)
408
+ });
409
+ });
354
410
  const renderedControls = (0, vue.computed)(() => {
355
411
  const value = props.controls !== void 0 ? typeof props.controls === "function" ? props.controls() : props.controls : slots.controls?.();
356
412
  return Array.isArray(value) ? value : value ? [value] : [];
357
413
  });
358
414
  const shouldDisplayControls = (0, vue.computed)(() => renderedControls.value.length > 0 || props.withExpandButton || props.withCopyButton);
359
- const lineNumbers = (0, vue.computed)(() => props.code.trim().split("\n").map((_, index) => index + 1));
415
+ const lineNumbers = (0, vue.computed)(() => normalizedCode.value.split("\n").map((_, index) => index + 1));
360
416
  const renderControls = () => renderedControls.value;
361
417
  provideCodeHighlightContext({
362
418
  getStyles,
@@ -382,7 +438,7 @@ var CodeHighlight_default = /* @__PURE__ */ (0, vue.defineComponent)({
382
438
  className: [highlighted.value.codeElementProps?.class, (0, vue.unref)(attrs).class],
383
439
  style: [highlighted.value.codeElementProps?.style, (0, vue.unref)(attrs).style]
384
440
  })
385
- }, { "data-with-border": (0, vue.unref)(props).withBorder || void 0 }), (0, vue.toDisplayString)((0, vue.unref)(props).code.trim()), 17, _hoisted_2)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_mantine_vue_core.Box), (0, vue.mergeProps)({ key: 2 }, {
441
+ }, { "data-with-border": (0, vue.unref)(props).withBorder || void 0 }), (0, vue.toDisplayString)(normalizedCode.value), 17, _hoisted_2)) : ((0, vue.openBlock)(), (0, vue.createBlock)((0, vue.unref)(_mantine_vue_core.Box), (0, vue.mergeProps)({ key: 2 }, {
386
442
  ...(0, vue.unref)(attrs),
387
443
  ...(0, vue.unref)(getStyles)("codeHighlight")
388
444
  }, {
@@ -407,7 +463,7 @@ var CodeHighlight_default = /* @__PURE__ */ (0, vue.defineComponent)({
407
463
  ])) : (0, vue.createCommentVNode)("", true),
408
464
  (0, vue.unref)(props).withCopyButton ? ((0, vue.openBlock)(), (0, vue.createBlock)(CopyCodeButton_default, {
409
465
  key: 1,
410
- code: (0, vue.unref)(props).code,
466
+ code: normalizedCode.value,
411
467
  "copied-label": (0, vue.unref)(props).copiedLabel,
412
468
  "copy-label": (0, vue.unref)(props).copyLabel
413
469
  }, null, 8, [
@@ -438,7 +494,7 @@ var CodeHighlight_default = /* @__PURE__ */ (0, vue.defineComponent)({
438
494
  className: highlighted.value.codeElementProps?.class,
439
495
  style: highlighted.value.codeElementProps?.style
440
496
  })
441
- })), (0, vue.toDisplayString)((0, vue.unref)(props).code.trim()), 17))], 16, _hoisted_5)], 16)]),
497
+ })), (0, vue.toDisplayString)(normalizedCode.value), 17))], 16, _hoisted_5)], 16)]),
442
498
  _: 1
443
499
  }, 16, ["data-collapsed"]),
444
500
  (0, vue.createVNode)((0, vue.unref)(_mantine_vue_core.UnstyledButton), (0, vue.mergeProps)((0, vue.unref)(getStyles)("showCodeButton"), {
@@ -483,6 +539,7 @@ var InlineCodeHighlight = Object.assign(/* @__PURE__ */ (0, vue.defineComponent)
483
539
  type: Boolean,
484
540
  default: false
485
541
  },
542
+ withFirstLineIndentation: { type: Boolean },
486
543
  classNames: {},
487
544
  styles: {},
488
545
  vars: {},
@@ -593,7 +650,8 @@ var CodeHighlightTabs = Object.assign(/* @__PURE__ */ (0, vue.defineComponent)({
593
650
  default: false
594
651
  },
595
652
  controls: {},
596
- codeColorScheme: {}
653
+ codeColorScheme: {},
654
+ withFirstLineIndentation: { type: Boolean }
597
655
  },
598
656
  emits: [
599
657
  "update:activeTab",
@@ -695,6 +753,7 @@ var CodeHighlightTabs = Object.assign(/* @__PURE__ */ (0, vue.defineComponent)({
695
753
  controls: (0, vue.unref)(props).controls,
696
754
  "code-color-scheme": (0, vue.unref)(props).codeColorScheme,
697
755
  "with-line-numbers": (0, vue.unref)(props).withLineNumbers,
756
+ "with-first-line-indentation": (0, vue.unref)(props).withFirstLineIndentation,
698
757
  "__with-offset": "",
699
758
  "__static-selector": "CodeHighlightTabs",
700
759
  onExpandedChange: (0, vue.unref)(setExpanded),
@@ -721,6 +780,7 @@ var CodeHighlightTabs = Object.assign(/* @__PURE__ */ (0, vue.defineComponent)({
721
780
  "controls",
722
781
  "code-color-scheme",
723
782
  "with-line-numbers",
783
+ "with-first-line-indentation",
724
784
  "onExpandedChange",
725
785
  "class-names",
726
786
  "styles"
@@ -736,7 +796,7 @@ function createHighlightJsAdapter(hljs) {
736
796
  return { getHighlighter: () => ({ code, language }) => {
737
797
  const lang = hljs.getLanguage(language) ? language : "plaintext";
738
798
  return {
739
- highlightedCode: hljs.highlight(code.trim(), { language: lang }).value,
799
+ highlightedCode: hljs.highlight(code, { language: lang }).value,
740
800
  isHighlighted: true,
741
801
  codeElementProps: { class: `hljs ${lang}` }
742
802
  };
@@ -1889,27 +1949,69 @@ function stripElement(openTag, data) {
1889
1949
  function stripShikiCodeBlocks(data) {
1890
1950
  return stripElement("code", stripElement("pre", data));
1891
1951
  }
1892
- var createShikiAdapter = (loadShiki, { forceColorScheme } = {}) => ({
1893
- loadContext: loadShiki,
1894
- getHighlighter: (ctx) => {
1895
- if (!ctx) return ({ code }) => ({
1896
- highlightedCode: code,
1897
- isHighlighted: false
1898
- });
1899
- return ({ code, language, colorScheme }) => {
1900
- let _colorScheme = colorScheme;
1901
- if (colorScheme === "light") _colorScheme = light;
1902
- else if (colorScheme === "dark") _colorScheme = dark;
1903
- return {
1904
- isHighlighted: true,
1905
- highlightedCode: stripShikiCodeBlocks(ctx.codeToHtml(code, {
1906
- lang: language,
1907
- theme: forceColorScheme || _colorScheme
1908
- }))
1952
+ var specialLanguages = [
1953
+ "text",
1954
+ "plaintext",
1955
+ "txt",
1956
+ "plain",
1957
+ "ansi"
1958
+ ];
1959
+ function isLanguageAvailable(ctx, language) {
1960
+ return !language || specialLanguages.includes(language) || typeof ctx.getLoadedLanguages !== "function" || ctx.getLoadedLanguages().includes(language);
1961
+ }
1962
+ var createShikiAdapter = (loadShiki, { forceColorScheme, resolveLanguage } = {}) => {
1963
+ const warnedLanguages = /* @__PURE__ */ new Set();
1964
+ const warnUnavailable = (language, reason) => {
1965
+ if (warnedLanguages.has(language)) return;
1966
+ warnedLanguages.add(language);
1967
+ console.warn(`[@mantine-vue/code-highlight] Language \`${language}\` ${reason}, code is rendered as plain text.`);
1968
+ };
1969
+ return {
1970
+ loadContext: loadShiki,
1971
+ loadLanguage: resolveLanguage ? (ctx, language) => {
1972
+ if (isLanguageAvailable(ctx, language)) return void 0;
1973
+ const onLoadError = (error) => {
1974
+ warnUnavailable(language, "could not be loaded");
1975
+ throw error;
1909
1976
  };
1910
- };
1911
- }
1912
- });
1977
+ try {
1978
+ const grammar = resolveLanguage(language);
1979
+ if (!grammar) {
1980
+ warnUnavailable(language, "was not resolved by `resolveLanguage`");
1981
+ return;
1982
+ }
1983
+ return Promise.resolve(ctx.loadLanguage(grammar)).catch(onLoadError);
1984
+ } catch (error) {
1985
+ return Promise.reject(error).catch(onLoadError);
1986
+ }
1987
+ } : void 0,
1988
+ getHighlighter: (ctx) => {
1989
+ if (!ctx) return ({ code }) => ({
1990
+ highlightedCode: code,
1991
+ isHighlighted: false
1992
+ });
1993
+ return ({ code, language, colorScheme }) => {
1994
+ if (!isLanguageAvailable(ctx, language)) {
1995
+ if (!resolveLanguage && language) warnUnavailable(language, "is not loaded");
1996
+ return {
1997
+ highlightedCode: code,
1998
+ isHighlighted: false
1999
+ };
2000
+ }
2001
+ let _colorScheme = colorScheme;
2002
+ if (colorScheme === "light") _colorScheme = light;
2003
+ else if (colorScheme === "dark") _colorScheme = dark;
2004
+ return {
2005
+ isHighlighted: true,
2006
+ highlightedCode: stripShikiCodeBlocks(ctx.codeToHtml(code, {
2007
+ lang: language,
2008
+ theme: forceColorScheme || _colorScheme
2009
+ }))
2010
+ };
2011
+ };
2012
+ }
2013
+ };
2014
+ };
1913
2015
  //#endregion
1914
2016
  exports.CodeHighlight = CodeHighlight;
1915
2017
  exports.CodeHighlightAdapterProvider = CodeHighlightAdapterProvider;
@@ -1918,9 +2020,12 @@ exports.CodeHighlightTabs = CodeHighlightTabs;
1918
2020
  exports.InlineCodeHighlight = InlineCodeHighlight;
1919
2021
  exports.createHighlightJsAdapter = createHighlightJsAdapter;
1920
2022
  exports.createShikiAdapter = createShikiAdapter;
2023
+ exports.normalizeCode = normalizeCode;
1921
2024
  exports.plainTextAdapter = plainTextAdapter;
1922
2025
  exports.stripShikiCodeBlocks = stripShikiCodeBlocks;
1923
2026
  exports.useCodeHighlightContext = useCodeHighlightContext;
1924
2027
  exports.useHighlight = useHighlight;
2028
+ exports.useIsLanguageLoaded = useIsLanguageLoaded;
2029
+ exports.useLoadLanguage = useLoadLanguage;
1925
2030
 
1926
2031
  //# sourceMappingURL=index.cjs.map