CodeBlock.tsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import React, { useMemo, useState } from "react"
  2. import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"
  3. import { oneDark } from "react-syntax-highlighter/dist/esm/styles/prism"
  4. import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
  5. import { getLanguageFromPath } from "../utilities/getLanguageFromPath"
  6. /*
  7. const vscodeSyntaxStyle: React.CSSProperties = {
  8. backgroundColor: "var(--vscode-editor-background)",
  9. color: "var(--vscode-editor-foreground)",
  10. fontFamily: "var(--vscode-editor-font-family)",
  11. fontSize: "var(--vscode-editor-font-size)",
  12. lineHeight: "var(--vscode-editor-line-height)",
  13. textAlign: "left",
  14. whiteSpace: "pre",
  15. wordSpacing: "normal",
  16. wordBreak: "normal",
  17. wordWrap: "normal",
  18. tabSize: 4,
  19. hyphens: "none",
  20. padding: "1em",
  21. margin: "0.5em 0",
  22. overflow: "auto",
  23. borderRadius: "6px",
  24. }
  25. const tokenStyles = {
  26. comment: { color: "var(--vscode-editor-foreground)" },
  27. prolog: { color: "var(--vscode-editor-foreground)" },
  28. doctype: { color: "var(--vscode-editor-foreground)" },
  29. cdata: { color: "var(--vscode-editor-foreground)" },
  30. punctuation: { color: "var(--vscode-editor-foreground)" },
  31. property: { color: "var(--vscode-symbolIcon-propertyForeground)" },
  32. tag: { color: "var(--vscode-symbolIcon-colorForeground)" },
  33. boolean: { color: "var(--vscode-symbolIcon-booleanForeground)" },
  34. number: { color: "var(--vscode-symbolIcon-numberForeground)" },
  35. constant: { color: "var(--vscode-symbolIcon-constantForeground)" },
  36. symbol: { color: "var(--vscode-symbolIcon-colorForeground)" },
  37. selector: { color: "var(--vscode-symbolIcon-colorForeground)" },
  38. "attr-name": { color: "var(--vscode-symbolIcon-propertyForeground)" },
  39. string: { color: "var(--vscode-symbolIcon-stringForeground)" },
  40. char: { color: "var(--vscode-symbolIcon-stringForeground)" },
  41. builtin: { color: "var(--vscode-symbolIcon-keywordForeground)" },
  42. inserted: { color: "var(--vscode-gitDecoration-addedResourceForeground)" },
  43. operator: { color: "var(--vscode-symbolIcon-operatorForeground)" },
  44. entity: { color: "var(--vscode-symbolIcon-snippetForeground)", cursor: "help" },
  45. url: { color: "var(--vscode-textLink-foreground)" },
  46. variable: { color: "var(--vscode-symbolIcon-variableForeground)" },
  47. atrule: { color: "var(--vscode-symbolIcon-keywordForeground)" },
  48. "attr-value": { color: "var(--vscode-symbolIcon-stringForeground)" },
  49. keyword: { color: "var(--vscode-symbolIcon-keywordForeground)" },
  50. function: { color: "var(--vscode-symbolIcon-functionForeground)" },
  51. regex: { color: "var(--vscode-symbolIcon-regexForeground)" },
  52. important: { color: "var(--vscode-editorWarning-foreground)", fontWeight: "bold" },
  53. bold: { fontWeight: "bold" },
  54. italic: { fontStyle: "italic" },
  55. deleted: { color: "var(--vscode-gitDecoration-deletedResourceForeground)" },
  56. }
  57. */
  58. interface CodeBlockProps {
  59. code?: string
  60. diff?: string
  61. language?: string | undefined
  62. path?: string
  63. }
  64. const CodeBlock = ({ code, diff, language, path }: CodeBlockProps) => {
  65. const [isExpanded, setIsExpanded] = useState(false)
  66. const backgroundColor = oneDark['pre[class*="language-"]'].background as string
  67. /*
  68. We need to remove leading non-alphanumeric characters from the path in order for our leading ellipses trick to work.
  69. ^: Anchors the match to the start of the string.
  70. [^a-zA-Z0-9]+: Matches one or more characters that are not alphanumeric.
  71. The replace method removes these matched characters, effectively trimming the string up to the first alphanumeric character.
  72. */
  73. const removeLeadingNonAlphanumeric = (path: string): string => path.replace(/^[^a-zA-Z0-9]+/, "")
  74. const inferredLanguage = useMemo(
  75. () => code && (language ?? (path ? getLanguageFromPath(path) : undefined)),
  76. [path, language, code]
  77. )
  78. console.log(inferredLanguage)
  79. return (
  80. <div
  81. style={{
  82. borderRadius: "3px",
  83. backgroundColor: backgroundColor,
  84. overflow: "hidden", // This ensures the inner scrollable area doesn't overflow the rounded corners
  85. }}>
  86. {path && (
  87. <div
  88. style={{
  89. display: "flex",
  90. justifyContent: "space-between",
  91. alignItems: "center",
  92. padding: "6px 10px",
  93. cursor: "pointer",
  94. }}
  95. onClick={() => setIsExpanded(!isExpanded)}>
  96. <span
  97. style={{
  98. color: "#BABCC3",
  99. whiteSpace: "nowrap",
  100. overflow: "hidden",
  101. textOverflow: "ellipsis",
  102. marginRight: "8px",
  103. // trick to get ellipsis at beginning of string
  104. direction: "rtl",
  105. textAlign: "left",
  106. }}>
  107. {removeLeadingNonAlphanumeric(path) + "\u200E"}
  108. </span>
  109. <VSCodeButton appearance="icon" aria-label="Toggle Code" onClick={() => setIsExpanded(!isExpanded)}>
  110. <span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
  111. </VSCodeButton>
  112. </div>
  113. )}
  114. {(!path || isExpanded) && (
  115. <div
  116. className="code-block-scrollable"
  117. style={{
  118. overflowX: "auto",
  119. overflowY: "hidden",
  120. maxWidth: "100%",
  121. }}>
  122. <SyntaxHighlighter
  123. wrapLines={false}
  124. language={diff ? "diff" : inferredLanguage} // "diff" automatically colors changed lines in green/red
  125. style={oneDark}
  126. customStyle={{
  127. margin: 0,
  128. padding: "6px 10px",
  129. borderRadius: 0,
  130. fontSize: "var(--vscode-editor-font-size)",
  131. lineHeight: "var(--vscode-editor-line-height)",
  132. }}>
  133. {code ?? diff ?? ""}
  134. </SyntaxHighlighter>
  135. </div>
  136. )}
  137. </div>
  138. )
  139. }
  140. export default CodeBlock