code.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. import { codeToHtml } from "shiki";
  2. import { CopyButton } from "./copy-button";
  3. import { ExpandableCode } from "./expandable-code";
  4. const vercelDarkTheme = {
  5. name: "vercel-dark",
  6. type: "dark" as const,
  7. colors: {
  8. "editor.background": "transparent",
  9. "editor.foreground": "#EDEDED",
  10. },
  11. settings: [
  12. {
  13. scope: ["comment", "punctuation.definition.comment"],
  14. settings: { foreground: "#666666" },
  15. },
  16. {
  17. scope: ["string", "string.quoted", "string.template"],
  18. settings: { foreground: "#50E3C2" },
  19. },
  20. {
  21. scope: [
  22. "constant.numeric",
  23. "constant.language.boolean",
  24. "constant.language.null",
  25. ],
  26. settings: { foreground: "#50E3C2" },
  27. },
  28. {
  29. scope: ["keyword", "storage.type", "storage.modifier"],
  30. settings: { foreground: "#FF0080" },
  31. },
  32. {
  33. scope: ["keyword.operator", "keyword.control"],
  34. settings: { foreground: "#FF0080" },
  35. },
  36. {
  37. scope: ["entity.name.function", "support.function", "meta.function-call"],
  38. settings: { foreground: "#7928CA" },
  39. },
  40. {
  41. scope: ["variable", "variable.other", "variable.parameter"],
  42. settings: { foreground: "#EDEDED" },
  43. },
  44. {
  45. scope: ["entity.name.tag", "support.class.component", "entity.name.type"],
  46. settings: { foreground: "#FF0080" },
  47. },
  48. {
  49. scope: ["punctuation", "meta.brace", "meta.bracket"],
  50. settings: { foreground: "#888888" },
  51. },
  52. {
  53. scope: [
  54. "support.type.property-name",
  55. "entity.name.tag.json",
  56. "meta.object-literal.key",
  57. ],
  58. settings: { foreground: "#EDEDED" },
  59. },
  60. {
  61. scope: ["entity.other.attribute-name"],
  62. settings: { foreground: "#50E3C2" },
  63. },
  64. {
  65. scope: ["support.type.primitive", "entity.name.type.primitive"],
  66. settings: { foreground: "#50E3C2" },
  67. },
  68. ],
  69. };
  70. const vercelLightTheme = {
  71. name: "vercel-light",
  72. type: "light" as const,
  73. colors: {
  74. "editor.background": "transparent",
  75. "editor.foreground": "#171717",
  76. },
  77. settings: [
  78. {
  79. scope: ["comment", "punctuation.definition.comment"],
  80. settings: { foreground: "#6b7280" },
  81. },
  82. {
  83. scope: ["string", "string.quoted", "string.template"],
  84. settings: { foreground: "#067a6e" },
  85. },
  86. {
  87. scope: [
  88. "constant.numeric",
  89. "constant.language.boolean",
  90. "constant.language.null",
  91. ],
  92. settings: { foreground: "#067a6e" },
  93. },
  94. {
  95. scope: ["keyword", "storage.type", "storage.modifier"],
  96. settings: { foreground: "#d6409f" },
  97. },
  98. {
  99. scope: ["keyword.operator", "keyword.control"],
  100. settings: { foreground: "#d6409f" },
  101. },
  102. {
  103. scope: ["entity.name.function", "support.function", "meta.function-call"],
  104. settings: { foreground: "#6e56cf" },
  105. },
  106. {
  107. scope: ["variable", "variable.other", "variable.parameter"],
  108. settings: { foreground: "#171717" },
  109. },
  110. {
  111. scope: ["entity.name.tag", "support.class.component", "entity.name.type"],
  112. settings: { foreground: "#d6409f" },
  113. },
  114. {
  115. scope: ["punctuation", "meta.brace", "meta.bracket"],
  116. settings: { foreground: "#6b7280" },
  117. },
  118. {
  119. scope: [
  120. "support.type.property-name",
  121. "entity.name.tag.json",
  122. "meta.object-literal.key",
  123. ],
  124. settings: { foreground: "#171717" },
  125. },
  126. {
  127. scope: ["entity.other.attribute-name"],
  128. settings: { foreground: "#067a6e" },
  129. },
  130. {
  131. scope: ["support.type.primitive", "entity.name.type.primitive"],
  132. settings: { foreground: "#067a6e" },
  133. },
  134. ],
  135. };
  136. interface CodeProps {
  137. children: string;
  138. lang?: "json" | "tsx" | "typescript" | "bash" | "javascript";
  139. }
  140. export async function Code({ children, lang = "typescript" }: CodeProps) {
  141. const html = await codeToHtml(children.trim(), {
  142. lang,
  143. themes: {
  144. light: vercelLightTheme,
  145. dark: vercelDarkTheme,
  146. },
  147. defaultColor: false,
  148. });
  149. return (
  150. <div className="group relative my-6 rounded-lg border border-border bg-neutral-100 dark:bg-[#0a0a0a] text-sm font-mono overflow-hidden max-w-full">
  151. <div className="absolute top-3 right-3 z-10">
  152. <CopyButton
  153. text={children.trim()}
  154. className="opacity-0 group-hover:opacity-100 text-neutral-500 dark:text-neutral-400 bg-neutral-100 dark:bg-[#0a0a0a]"
  155. />
  156. </div>
  157. <ExpandableCode>
  158. <div
  159. className="overflow-x-auto [&_pre]:bg-transparent! [&_pre]:m-0! [&_pre]:p-4! [&_code]:bg-transparent! [&_.shiki]:bg-transparent!"
  160. dangerouslySetInnerHTML={{ __html: html }}
  161. />
  162. </ExpandableCode>
  163. </div>
  164. );
  165. }