|
| 1 | +import { |
| 2 | + type FileContents, |
| 3 | + FileDiff, |
| 4 | + type DiffLineAnnotation, |
| 5 | + DiffFileRendererOptions, |
| 6 | + registerCustomTheme, |
| 7 | +} from "@pierre/precision-diffs" |
| 8 | +import { ComponentProps, createEffect, splitProps } from "solid-js" |
| 9 | + |
| 10 | +export type DiffProps<T = {}> = Omit<DiffFileRendererOptions<T>, "themes"> & { |
| 11 | + before: FileContents |
| 12 | + after: FileContents |
| 13 | + annotations?: DiffLineAnnotation<T>[] |
| 14 | + class?: string |
| 15 | + classList?: ComponentProps<"div">["classList"] |
| 16 | +} |
| 17 | + |
| 18 | +// @ts-expect-error |
| 19 | +registerCustomTheme("opencode", () => import("./theme.json")) |
| 20 | + |
| 21 | +// interface ThreadMetadata { |
| 22 | +// threadId: string |
| 23 | +// } |
| 24 | + |
| 25 | +export function Diff<T>(props: DiffProps<T>) { |
| 26 | + let container!: HTMLDivElement |
| 27 | + const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"]) |
| 28 | + |
| 29 | + // const lineAnnotations: DiffLineAnnotation<ThreadMetadata>[] = [ |
| 30 | + // { |
| 31 | + // side: "additions", |
| 32 | + // // The line number specified for an annotation is the visual line number |
| 33 | + // // you see in the number column of a diff |
| 34 | + // lineNumber: 16, |
| 35 | + // metadata: { threadId: "68b329da9893e34099c7d8ad5cb9c940" }, |
| 36 | + // }, |
| 37 | + // ] |
| 38 | + |
| 39 | + // If you ever want to update the options for an instance, simple call |
| 40 | + // 'setOptions' with the new options. Bear in mind, this does NOT merge |
| 41 | + // existing properties, it's a full replace |
| 42 | + // instance.setOptions({ |
| 43 | + // ...instance.options, |
| 44 | + // theme: "pierre-dark", |
| 45 | + // themes: undefined, |
| 46 | + // }) |
| 47 | + |
| 48 | + // When ready to render, simply call .render with old/new file, optional |
| 49 | + // annotations and a container element to hold the diff |
| 50 | + createEffect(() => { |
| 51 | + const instance = new FileDiff<T>({ |
| 52 | + theme: "opencode", |
| 53 | + // Or can also provide a 'themes' prop, which allows the code to adapt |
| 54 | + // to your OS light or dark theme |
| 55 | + // themes: { dark: 'pierre-night', light: 'pierre-light' }, |
| 56 | + // When using the 'themes' prop, 'themeType' allows you to force 'dark' |
| 57 | + // or 'light' theme, or inherit from the OS ('system') theme. |
| 58 | + themeType: "system", |
| 59 | + // Disable the line numbers for your diffs, generally not recommended |
| 60 | + disableLineNumbers: false, |
| 61 | + // Whether code should 'wrap' with long lines or 'scroll'. |
| 62 | + overflow: "scroll", |
| 63 | + // Normally you shouldn't need this prop, but if you don't provide a |
| 64 | + // valid filename or your file doesn't have an extension you may want to |
| 65 | + // override the automatic detection. You can specify that language here: |
| 66 | + // https://shiki.style/languages |
| 67 | + // lang?: SupportedLanguages; |
| 68 | + // 'diffStyle' controls whether the diff is presented side by side or |
| 69 | + // in a unified (single column) view |
| 70 | + diffStyle: "unified", |
| 71 | + // Line decorators to help highlight changes. |
| 72 | + // 'bars' (default): |
| 73 | + // Shows some red-ish or green-ish (theme dependent) bars on the left |
| 74 | + // edge of relevant lines |
| 75 | + // |
| 76 | + // 'classic': |
| 77 | + // shows '+' characters on additions and '-' characters on deletions |
| 78 | + // |
| 79 | + // 'none': |
| 80 | + // No special diff indicators are shown |
| 81 | + diffIndicators: "bars", |
| 82 | + // By default green-ish or red-ish background are shown on added and |
| 83 | + // deleted lines respectively. Disable that feature here |
| 84 | + disableBackground: false, |
| 85 | + // Diffs are split up into hunks, this setting customizes what to show |
| 86 | + // between each hunk. |
| 87 | + // |
| 88 | + // 'line-info' (default): |
| 89 | + // Shows a bar that tells you how many lines are collapsed. If you are |
| 90 | + // using the oldFile/newFile API then you can click those bars to |
| 91 | + // expand the content between them |
| 92 | + // |
| 93 | + // 'metadata': |
| 94 | + // Shows the content you'd see in a normal patch file, usually in some |
| 95 | + // format like '@@ -60,6 +60,22 @@'. You cannot use these to expand |
| 96 | + // hidden content |
| 97 | + // |
| 98 | + // 'simple': |
| 99 | + // Just a subtle bar separator between each hunk |
| 100 | + hunkSeparators: "line-info", |
| 101 | + // On lines that have both additions and deletions, we can run a |
| 102 | + // separate diff check to mark parts of the lines that change. |
| 103 | + // 'none': |
| 104 | + // Do not show these secondary highlights |
| 105 | + // |
| 106 | + // 'char': |
| 107 | + // Show changes at a per character granularity |
| 108 | + // |
| 109 | + // 'word': |
| 110 | + // Show changes but rounded up to word boundaries |
| 111 | + // |
| 112 | + // 'word-alt' (default): |
| 113 | + // Similar to 'word', however we attempt to minimize single character |
| 114 | + // gaps between highlighted changes |
| 115 | + lineDiffType: "word-alt", |
| 116 | + // If lines exceed these character lengths then we won't perform the |
| 117 | + // line lineDiffType check |
| 118 | + maxLineDiffLength: 1000, |
| 119 | + // If any line in the diff exceeds this value then we won't attempt to |
| 120 | + // syntax highlight the diff |
| 121 | + maxLineLengthForHighlighting: 1000, |
| 122 | + // Enabling this property will hide the file header with file name and |
| 123 | + // diff stats. |
| 124 | + disableFileHeader: true, |
| 125 | + // You can optionally pass a render function for rendering out line |
| 126 | + // annotations. Just return the dom node to render |
| 127 | + // renderAnnotation(annotation: DiffLineAnnotation<T>): HTMLElement { |
| 128 | + // // Despite the diff itself being rendered in the shadow dom, |
| 129 | + // // annotations are inserted via the web components 'slots' api and you |
| 130 | + // // can use all your normal normal css and styling for them |
| 131 | + // const element = document.createElement("div") |
| 132 | + // element.innerText = annotation.metadata.threadId |
| 133 | + // return element |
| 134 | + // }, |
| 135 | + ...others, |
| 136 | + }) |
| 137 | + |
| 138 | + instance.render({ |
| 139 | + oldFile: local.before, |
| 140 | + newFile: local.after, |
| 141 | + lineAnnotations: local.annotations, |
| 142 | + containerWrapper: container, |
| 143 | + }) |
| 144 | + }) |
| 145 | + |
| 146 | + return ( |
| 147 | + <div |
| 148 | + style={{ |
| 149 | + "--pjs-font-family": "var(--font-family-mono)", |
| 150 | + "--pjs-font-size": "var(--font-size-small)", |
| 151 | + "--pjs-line-height": "24px", |
| 152 | + "--pjs-tab-size": 4, |
| 153 | + "--pjs-font-features": "var(--font-family-mono--font-feature-settings)", |
| 154 | + "--pjs-header-font-family": "var(--font-family-sans)", |
| 155 | + }} |
| 156 | + ref={container} |
| 157 | + /> |
| 158 | + ) |
| 159 | +} |
0 commit comments