code.tsx 4.5 KB

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