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