@mistweaverco/mdsvex-shiki 1.0.16 → 1.1.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/README.md CHANGED
@@ -1,21 +1,47 @@
1
1
  # mdsvex-shiki
2
2
 
3
- Supports most [common transformers][shiki-transformers] out of the box.
3
+ A highlighter for mdsvex using Shiki with
4
+ support for most [common transformers][shiki-transformers] out of the box.
5
+
6
+ Why? Because mdsvex's built-in Prism highlighter is
7
+ limited in features and customization options.
8
+
9
+ We needed a better solution for our SvelteKit projects,
10
+ so we created this package to fill the gap.
11
+
12
+ <img width="945" height="874" alt="Screenshot" src="https://github.com/user-attachments/assets/c717f559-ae96-4372-a1ac-3eba3d4db340" />
4
13
 
5
14
  ## Installation
6
15
 
7
- Using bun:
16
+ Using your package manager of choice, run:
8
17
 
9
18
  ```bash
10
- bun add @mistweaverco/mdsvex-shiki#v1.0.15
19
+ # npm
20
+ npm install @mistweaverco/mdsvex-shiki@v1.1.0
21
+
22
+ # yarn
23
+ yarn add @mistweaverco/mdsvex-shiki@v1.1.0
24
+
25
+ # bun
26
+ bun add @mistweaverco/mdsvex-shiki@v1.1.0
27
+
28
+ # pnpm
29
+ pnpm add @mistweaverco/mdsvex-shiki@v1.1.0
30
+
31
+ # deno
32
+ deno add npm:@mistweaverco/mdsvex-shiki@v1.1.0
11
33
  ```
12
34
 
13
35
  ## Configuration
14
36
 
15
37
  Available options are as follows:
16
38
 
17
- - `displayTitle`: Whether to show the title bar when a title is
18
- provided in the code block info string. (default: `false`)
39
+ - `displayPath`: Whether to show the file path in the title bar when
40
+ a path is provided. (default: `false`)
41
+ (e.g., `sh path=/path/to/file.sh`).
42
+ Paths are displayed with directory and file icons.
43
+ Long paths are collapsed
44
+ with a tooltip showing the full path.
19
45
  - `displayLang`: Whether to show the language label in
20
46
  the title bar. (default: `false`)
21
47
  - `disableCopyButton`: Whether to disable the copy button.
@@ -33,7 +59,7 @@ Available options are as follows:
33
59
 
34
60
  ```js
35
61
  const options = {
36
- displayTitle: true,
62
+ displayPath: true,
37
63
  displayLang: true,
38
64
  shikiOptions: {
39
65
  theme: 'nord',
@@ -46,7 +72,7 @@ const options = {
46
72
  If you just want to basic Shiki styling without the bar,
47
73
  you don't need to import this CSS file.
48
74
 
49
- If you to display the title, language and/or copy button,
75
+ If you want to display the path, language and/or copy button,
50
76
  you **must** import the CSS file.
51
77
 
52
78
  Probably in the root layout or HTML file that wraps your markdown content.
@@ -104,7 +130,7 @@ const config = {
104
130
  // ...
105
131
  highlight: {
106
132
  highlighter: await mdsvexShiki({
107
- displayTitle: true,
133
+ displayPath: true,
108
134
  displayLang: true,
109
135
  shikiOptions: {
110
136
  // Shiki options
@@ -4713,9 +4713,53 @@ function extractText(node) {
4713
4713
  }
4714
4714
  return "";
4715
4715
  }
4716
+ function parseMetaString(metaString) {
4717
+ if (!metaString) {
4718
+ return {
4719
+ path: undefined
4720
+ };
4721
+ }
4722
+ const pathMatch = metaString.match(/(?:^|\s)path=(?:"([^"]+)"|([^\s]+))/);
4723
+ const pathValue = pathMatch?.[1] ?? pathMatch?.[2];
4724
+ return {
4725
+ path: pathValue
4726
+ };
4727
+ }
4728
+ function formatPathSegments(path) {
4729
+ const normalizedPath = path.replace(/\\/g, "/");
4730
+ const segments = normalizedPath.split("/").filter((s) => s.length > 0);
4731
+ if (segments.length === 0) {
4732
+ return [];
4733
+ }
4734
+ if (segments.length === 1) {
4735
+ return [{ type: "file", value: segments[0] ?? "" }];
4736
+ }
4737
+ const result = [];
4738
+ const directories = segments.slice(0, -1);
4739
+ const file = segments[segments.length - 1];
4740
+ if (directories.length > 2) {
4741
+ result.push({ type: "directory", value: directories[0] ?? "" });
4742
+ result.push({ type: "directory", value: ".." });
4743
+ result.push({
4744
+ type: "directory",
4745
+ value: directories[directories.length - 1] ?? ""
4746
+ });
4747
+ } else {
4748
+ directories.forEach((dir) => {
4749
+ result.push({ type: "directory", value: dir ?? "" });
4750
+ });
4751
+ }
4752
+ result.push({ type: "file", value: file ?? "" });
4753
+ return result;
4754
+ }
4716
4755
 
4717
4756
  // src/transformers.ts
4718
- var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4757
+ var mdsvexWrapItUpTransformer = (lang, codeText, meta, options = {}) => {
4758
+ const {
4759
+ disableCopyButton = false,
4760
+ displayPath = true,
4761
+ displayLang = true
4762
+ } = options;
4719
4763
  return {
4720
4764
  name: "transformerMdsvexWrapItUp",
4721
4765
  enforce: "post",
@@ -4733,11 +4777,86 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4733
4777
  }
4734
4778
  });
4735
4779
  }
4736
- const headerChildren = [
4737
- {
4780
+ const headerChildren = [];
4781
+ const { path } = parseMetaString(meta);
4782
+ if (displayPath && path) {
4783
+ const segments = formatPathSegments(path);
4784
+ const allSegments = path.split(/[/\\]/).filter((s) => s.length > 0);
4785
+ const hasCollapsedSegments = allSegments.length > 3;
4786
+ const fullPath = allSegments.join("/");
4787
+ const pathChildren = [];
4788
+ segments.forEach((segment, index) => {
4789
+ const isDirectory = segment.type === "directory";
4790
+ const isCollapsed = segment.value === "..";
4791
+ const dirIconPath = "M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z";
4792
+ const fileIconPath = "M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V304H176c-8.8 0-16 7.2-16 16s7.2 16 16 16H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zM384 128H256V0L384 128z";
4793
+ const properties = {
4794
+ class: `path-segment ${isDirectory ? "directory" : "file"} ${isCollapsed ? "collapsed" : ""}`
4795
+ };
4796
+ if (isCollapsed && hasCollapsedSegments) {
4797
+ properties.title = fullPath;
4798
+ }
4799
+ pathChildren.push({
4800
+ type: "element",
4801
+ tagName: "span",
4802
+ properties,
4803
+ children: [
4804
+ {
4805
+ type: "element",
4806
+ tagName: "svg",
4807
+ properties: {
4808
+ class: "icon",
4809
+ width: "14",
4810
+ height: "14",
4811
+ viewBox: "0 0 512 512",
4812
+ fill: "currentColor",
4813
+ "aria-hidden": "true"
4814
+ },
4815
+ children: [
4816
+ {
4817
+ type: "element",
4818
+ tagName: "path",
4819
+ properties: {
4820
+ d: isDirectory ? dirIconPath : fileIconPath
4821
+ },
4822
+ children: []
4823
+ }
4824
+ ]
4825
+ },
4826
+ {
4827
+ type: "text",
4828
+ value: segment.value
4829
+ }
4830
+ ]
4831
+ });
4832
+ if (index < segments.length - 1) {
4833
+ pathChildren.push({
4834
+ type: "element",
4835
+ tagName: "span",
4836
+ properties: { class: "path-separator" },
4837
+ children: [
4838
+ {
4839
+ type: "text",
4840
+ value: "/"
4841
+ }
4842
+ ]
4843
+ });
4844
+ }
4845
+ });
4846
+ headerChildren.push({
4847
+ type: "element",
4848
+ tagName: "span",
4849
+ properties: { class: "path" },
4850
+ children: pathChildren
4851
+ });
4852
+ }
4853
+ if (displayLang) {
4854
+ headerChildren.push({
4738
4855
  type: "element",
4739
4856
  tagName: "span",
4740
- properties: { class: "language" },
4857
+ properties: {
4858
+ class: "language"
4859
+ },
4741
4860
  children: [
4742
4861
  {
4743
4862
  type: "element",
@@ -4766,8 +4885,8 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4766
4885
  value: lang
4767
4886
  }
4768
4887
  ]
4769
- }
4770
- ];
4888
+ });
4889
+ }
4771
4890
  if (!disableCopyButton) {
4772
4891
  headerChildren.push({
4773
4892
  type: "element",
@@ -4837,20 +4956,20 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4837
4956
  tabindex: "0"
4838
4957
  },
4839
4958
  children: [
4840
- ...children.map((child) => {
4959
+ ...children.flatMap((child) => {
4841
4960
  if (child.type === "element" && child.tagName === "pre") {
4842
- return {
4843
- ...child,
4844
- children: [
4845
- {
4846
- type: "element",
4847
- tagName: "div",
4848
- properties: { class: "header" },
4849
- children: headerChildren
4850
- },
4851
- ...child.children
4852
- ]
4853
- };
4961
+ return [
4962
+ {
4963
+ type: "element",
4964
+ tagName: "div",
4965
+ properties: { class: "header" },
4966
+ children: headerChildren
4967
+ },
4968
+ {
4969
+ ...child,
4970
+ children: child.children
4971
+ }
4972
+ ];
4854
4973
  }
4855
4974
  return child;
4856
4975
  })
@@ -4878,7 +4997,7 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4878
4997
  };
4879
4998
  };
4880
4999
 
4881
- // node_modules/shiki/node_modules/@shikijs/types/dist/index.mjs
5000
+ // node_modules/@shikijs/types/dist/index.mjs
4882
5001
  class ShikiError extends Error {
4883
5002
  constructor(message) {
4884
5003
  super(message);
@@ -9629,7 +9748,7 @@ function all(parent) {
9629
9748
  }
9630
9749
  return results.join("");
9631
9750
  }
9632
- // node_modules/shiki/node_modules/@shikijs/core/dist/index.mjs
9751
+ // node_modules/@shikijs/core/dist/index.mjs
9633
9752
  function resolveColorReplacements(theme, options) {
9634
9753
  const replacements = typeof theme === "string" ? {} : { ...theme.colorReplacements };
9635
9754
  const themeName = typeof theme === "string" ? theme : theme.name;
@@ -13814,7 +13933,7 @@ var mdsvexShiki = async (config) => {
13814
13933
  delete shikiOptions.themes;
13815
13934
  delete shikiOptions.langs;
13816
13935
  await getHighlighterInstance(themes, langs);
13817
- return async (code, lang245) => {
13936
+ return async (code, lang245, meta) => {
13818
13937
  lang245 = lang245 ?? "text";
13819
13938
  const highlighter = await getHighlighterInstance(themes, langs);
13820
13939
  const transformers = [
@@ -13822,7 +13941,7 @@ var mdsvexShiki = async (config) => {
13822
13941
  ...shikiOptions.transformers || []
13823
13942
  ];
13824
13943
  if (!transformers.find((t) => t.name === "transformerMdsvexWrapItUp")) {
13825
- transformers.push(mdsvexWrapItUpTransformer(lang245, code, config.disableCopyButton));
13944
+ transformers.push(mdsvexWrapItUpTransformer(lang245, code, meta, config));
13826
13945
  }
13827
13946
  const html5 = highlighter.codeToHtml(code, {
13828
13947
  ...shikiOptions,
@@ -16,7 +16,7 @@ import { Action } from 'svelte/action';
16
16
  */
17
17
  export declare const copyAction: Action<HTMLElement>;
18
18
  export type HighlighterOptions = {
19
- displayTitle?: boolean;
19
+ displayPath?: boolean;
20
20
  displayLanguage?: boolean;
21
21
  disableCopyButton?: boolean;
22
22
  shikiOptions?: Partial<CodeToHastOptions<BundledLanguage, BundledTheme>> & {
@@ -28,7 +28,7 @@ export type HighlighterOptions = {
28
28
  };
29
29
  };
30
30
  export declare const defaultShikiOptions: Partial<CodeToHastOptions<BundledLanguage, BundledTheme>>;
31
- export declare const mdsvexShiki: (config: HighlighterOptions) => Promise<(code: string, lang: string) => Promise<string>>;
31
+ export declare const mdsvexShiki: (config: HighlighterOptions) => Promise<(code: string, lang: string, meta?: string) => Promise<string>>;
32
32
 
33
33
  export {
34
34
  mdsvexShiki as default,
@@ -4686,9 +4686,53 @@ function extractText(node) {
4686
4686
  }
4687
4687
  return "";
4688
4688
  }
4689
+ function parseMetaString(metaString) {
4690
+ if (!metaString) {
4691
+ return {
4692
+ path: undefined
4693
+ };
4694
+ }
4695
+ const pathMatch = metaString.match(/(?:^|\s)path=(?:"([^"]+)"|([^\s]+))/);
4696
+ const pathValue = pathMatch?.[1] ?? pathMatch?.[2];
4697
+ return {
4698
+ path: pathValue
4699
+ };
4700
+ }
4701
+ function formatPathSegments(path) {
4702
+ const normalizedPath = path.replace(/\\/g, "/");
4703
+ const segments = normalizedPath.split("/").filter((s) => s.length > 0);
4704
+ if (segments.length === 0) {
4705
+ return [];
4706
+ }
4707
+ if (segments.length === 1) {
4708
+ return [{ type: "file", value: segments[0] ?? "" }];
4709
+ }
4710
+ const result = [];
4711
+ const directories = segments.slice(0, -1);
4712
+ const file = segments[segments.length - 1];
4713
+ if (directories.length > 2) {
4714
+ result.push({ type: "directory", value: directories[0] ?? "" });
4715
+ result.push({ type: "directory", value: ".." });
4716
+ result.push({
4717
+ type: "directory",
4718
+ value: directories[directories.length - 1] ?? ""
4719
+ });
4720
+ } else {
4721
+ directories.forEach((dir) => {
4722
+ result.push({ type: "directory", value: dir ?? "" });
4723
+ });
4724
+ }
4725
+ result.push({ type: "file", value: file ?? "" });
4726
+ return result;
4727
+ }
4689
4728
 
4690
4729
  // src/transformers.ts
4691
- var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4730
+ var mdsvexWrapItUpTransformer = (lang, codeText, meta, options = {}) => {
4731
+ const {
4732
+ disableCopyButton = false,
4733
+ displayPath = true,
4734
+ displayLang = true
4735
+ } = options;
4692
4736
  return {
4693
4737
  name: "transformerMdsvexWrapItUp",
4694
4738
  enforce: "post",
@@ -4706,11 +4750,86 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4706
4750
  }
4707
4751
  });
4708
4752
  }
4709
- const headerChildren = [
4710
- {
4753
+ const headerChildren = [];
4754
+ const { path } = parseMetaString(meta);
4755
+ if (displayPath && path) {
4756
+ const segments = formatPathSegments(path);
4757
+ const allSegments = path.split(/[/\\]/).filter((s) => s.length > 0);
4758
+ const hasCollapsedSegments = allSegments.length > 3;
4759
+ const fullPath = allSegments.join("/");
4760
+ const pathChildren = [];
4761
+ segments.forEach((segment, index) => {
4762
+ const isDirectory = segment.type === "directory";
4763
+ const isCollapsed = segment.value === "..";
4764
+ const dirIconPath = "M64 480H448c35.3 0 64-28.7 64-64V160c0-35.3-28.7-64-64-64H288c-10.1 0-19.6-4.7-25.6-12.8L243.2 57.6C231.1 41.5 212.1 32 192 32H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64z";
4765
+ const fileIconPath = "M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V304H176c-8.8 0-16 7.2-16 16s7.2 16 16 16H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zM384 128H256V0L384 128z";
4766
+ const properties = {
4767
+ class: `path-segment ${isDirectory ? "directory" : "file"} ${isCollapsed ? "collapsed" : ""}`
4768
+ };
4769
+ if (isCollapsed && hasCollapsedSegments) {
4770
+ properties.title = fullPath;
4771
+ }
4772
+ pathChildren.push({
4773
+ type: "element",
4774
+ tagName: "span",
4775
+ properties,
4776
+ children: [
4777
+ {
4778
+ type: "element",
4779
+ tagName: "svg",
4780
+ properties: {
4781
+ class: "icon",
4782
+ width: "14",
4783
+ height: "14",
4784
+ viewBox: "0 0 512 512",
4785
+ fill: "currentColor",
4786
+ "aria-hidden": "true"
4787
+ },
4788
+ children: [
4789
+ {
4790
+ type: "element",
4791
+ tagName: "path",
4792
+ properties: {
4793
+ d: isDirectory ? dirIconPath : fileIconPath
4794
+ },
4795
+ children: []
4796
+ }
4797
+ ]
4798
+ },
4799
+ {
4800
+ type: "text",
4801
+ value: segment.value
4802
+ }
4803
+ ]
4804
+ });
4805
+ if (index < segments.length - 1) {
4806
+ pathChildren.push({
4807
+ type: "element",
4808
+ tagName: "span",
4809
+ properties: { class: "path-separator" },
4810
+ children: [
4811
+ {
4812
+ type: "text",
4813
+ value: "/"
4814
+ }
4815
+ ]
4816
+ });
4817
+ }
4818
+ });
4819
+ headerChildren.push({
4820
+ type: "element",
4821
+ tagName: "span",
4822
+ properties: { class: "path" },
4823
+ children: pathChildren
4824
+ });
4825
+ }
4826
+ if (displayLang) {
4827
+ headerChildren.push({
4711
4828
  type: "element",
4712
4829
  tagName: "span",
4713
- properties: { class: "language" },
4830
+ properties: {
4831
+ class: "language"
4832
+ },
4714
4833
  children: [
4715
4834
  {
4716
4835
  type: "element",
@@ -4739,8 +4858,8 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4739
4858
  value: lang
4740
4859
  }
4741
4860
  ]
4742
- }
4743
- ];
4861
+ });
4862
+ }
4744
4863
  if (!disableCopyButton) {
4745
4864
  headerChildren.push({
4746
4865
  type: "element",
@@ -4810,20 +4929,20 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4810
4929
  tabindex: "0"
4811
4930
  },
4812
4931
  children: [
4813
- ...children.map((child) => {
4932
+ ...children.flatMap((child) => {
4814
4933
  if (child.type === "element" && child.tagName === "pre") {
4815
- return {
4816
- ...child,
4817
- children: [
4818
- {
4819
- type: "element",
4820
- tagName: "div",
4821
- properties: { class: "header" },
4822
- children: headerChildren
4823
- },
4824
- ...child.children
4825
- ]
4826
- };
4934
+ return [
4935
+ {
4936
+ type: "element",
4937
+ tagName: "div",
4938
+ properties: { class: "header" },
4939
+ children: headerChildren
4940
+ },
4941
+ {
4942
+ ...child,
4943
+ children: child.children
4944
+ }
4945
+ ];
4827
4946
  }
4828
4947
  return child;
4829
4948
  })
@@ -4851,7 +4970,7 @@ var mdsvexWrapItUpTransformer = (lang, codeText, disableCopyButton) => {
4851
4970
  };
4852
4971
  };
4853
4972
 
4854
- // node_modules/shiki/node_modules/@shikijs/types/dist/index.mjs
4973
+ // node_modules/@shikijs/types/dist/index.mjs
4855
4974
  class ShikiError extends Error {
4856
4975
  constructor(message) {
4857
4976
  super(message);
@@ -9602,7 +9721,7 @@ function all(parent) {
9602
9721
  }
9603
9722
  return results.join("");
9604
9723
  }
9605
- // node_modules/shiki/node_modules/@shikijs/core/dist/index.mjs
9724
+ // node_modules/@shikijs/core/dist/index.mjs
9606
9725
  function resolveColorReplacements(theme, options) {
9607
9726
  const replacements = typeof theme === "string" ? {} : { ...theme.colorReplacements };
9608
9727
  const themeName = typeof theme === "string" ? theme : theme.name;
@@ -13787,7 +13906,7 @@ var mdsvexShiki = async (config) => {
13787
13906
  delete shikiOptions.themes;
13788
13907
  delete shikiOptions.langs;
13789
13908
  await getHighlighterInstance(themes, langs);
13790
- return async (code, lang245) => {
13909
+ return async (code, lang245, meta) => {
13791
13910
  lang245 = lang245 ?? "text";
13792
13911
  const highlighter = await getHighlighterInstance(themes, langs);
13793
13912
  const transformers = [
@@ -13795,7 +13914,7 @@ var mdsvexShiki = async (config) => {
13795
13914
  ...shikiOptions.transformers || []
13796
13915
  ];
13797
13916
  if (!transformers.find((t) => t.name === "transformerMdsvexWrapItUp")) {
13798
- transformers.push(mdsvexWrapItUpTransformer(lang245, code, config.disableCopyButton));
13917
+ transformers.push(mdsvexWrapItUpTransformer(lang245, code, meta, config));
13799
13918
  }
13800
13919
  const html5 = highlighter.codeToHtml(code, {
13801
13920
  ...shikiOptions,
package/package.json CHANGED
@@ -1,30 +1,28 @@
1
1
  {
2
2
  "name": "@mistweaverco/mdsvex-shiki",
3
- "version": "1.0.16",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "main": "./index.cjs",
6
6
  "module": "./index.js",
7
7
  "types": "./index.d.ts",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./index.d.ts",
11
- "import": "./index.js",
12
- "require": "./index.cjs"
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "require": "./dist/index.cjs"
13
13
  },
14
14
  "./styles.css": "./styles.css"
15
15
  },
16
16
  "files": [
17
- "styles.css",
18
- "*.js",
19
- "*.cjs",
20
- "*.d.ts"
17
+ "dist/*.{js,cjs,d.ts,css}",
18
+ "styles.css"
21
19
  ],
22
20
  "scripts": {
23
21
  "test": "echo \"Error: no test specified\" && exit 1",
24
22
  "prepublishOnly": "bun run build",
25
23
  "clear": "rm -f *.js *.cjs *.d.ts",
26
24
  "build": "bun build.ts",
27
- "publish:npm": "npm publish",
25
+ "publish:npm": "npm publish --access public",
28
26
  "publish:github": "npm publish --registry https://npm.pkg.github.com --access public"
29
27
  },
30
28
  "keywords": [
package/styles.css CHANGED
@@ -25,7 +25,7 @@
25
25
  }
26
26
 
27
27
  .mdsvex-shiki {
28
- padding: 5px 15px;
28
+ padding: 12px 20px;
29
29
  background-color: var(--shiki-dark-bg);
30
30
  position: relative;
31
31
  border: 1px solid var(--shiki-dark-border);
@@ -38,20 +38,16 @@
38
38
  }
39
39
 
40
40
  .mdsvex-shiki .header {
41
- position: absolute;
42
- top: 0;
43
- right: 0;
44
- z-index: 10;
45
41
  background-color: transparent;
46
- padding: 0.5em 1em;
47
42
  pointer-events: none;
43
+ display: flex;
44
+ align-items: center;
45
+ gap: 0.5em;
48
46
  }
49
47
 
50
48
  .mdsvex-shiki .header > * {
51
49
  pointer-events: auto;
52
- position: absolute;
53
- top: 0;
54
- right: 0;
50
+ position: relative;
55
51
  transition: opacity 0.2s ease;
56
52
  }
57
53
 
@@ -62,6 +58,7 @@
62
58
  display: flex;
63
59
  align-items: center;
64
60
  gap: 0.5em;
61
+ margin-left: auto;
65
62
  }
66
63
 
67
64
  .mdsvex-shiki .header .language .icon {
@@ -70,28 +67,17 @@
70
67
  flex-shrink: 0;
71
68
  }
72
69
 
73
- .mdsvex-shiki:hover .header .language,
74
- .mdsvex-shiki:focus-within .header .language {
75
- opacity: 0;
76
- }
77
-
78
70
  .mdsvex-shiki .header .copy {
79
71
  color: var(--shiki-dark);
80
72
  background-color: transparent;
81
73
  border: none;
82
- padding: 0.25em 0.5em;
83
74
  cursor: pointer;
84
- opacity: 0;
75
+ opacity: 1;
85
76
  display: flex;
86
77
  align-items: center;
87
78
  justify-content: center;
88
79
  }
89
80
 
90
- .mdsvex-shiki:hover .header .copy,
91
- .mdsvex-shiki:focus-within .header .copy {
92
- opacity: 1;
93
- }
94
-
95
81
  .mdsvex-shiki .header .copy .icon {
96
82
  width: 16px;
97
83
  height: 16px;
@@ -118,3 +104,42 @@
118
104
  color: var(--shiki-dark-accent-active);
119
105
  }
120
106
 
107
+ .mdsvex-shiki .header .path {
108
+ color: var(--shiki-dark);
109
+ font-size: 0.875em;
110
+ opacity: 0.7;
111
+ display: flex;
112
+ align-items: center;
113
+ gap: 0.25em;
114
+ max-width: calc(100% - 100px);
115
+ overflow: hidden;
116
+ white-space: nowrap;
117
+ }
118
+
119
+ .mdsvex-shiki .header .path .icon {
120
+ width: 14px;
121
+ height: 14px;
122
+ flex-shrink: 0;
123
+ }
124
+
125
+ .mdsvex-shiki .header .path .path-segment {
126
+ display: flex;
127
+ align-items: center;
128
+ gap: 0.25em;
129
+ }
130
+
131
+ .mdsvex-shiki .header .path .path-separator {
132
+ color: var(--shiki-dark);
133
+ opacity: 0.5;
134
+ margin: 0 0.125em;
135
+ }
136
+
137
+ .mdsvex-shiki .header .path .path-segment.collapsed {
138
+ cursor: help;
139
+ }
140
+
141
+ .mdsvex-shiki:hover .header .path,
142
+ .mdsvex-shiki:focus-within .header .path {
143
+ opacity: 1;
144
+ }
145
+