From 77ae0b527e4b1a8277b0391d0acbc7d82b08e4ea Mon Sep 17 00:00:00 2001 From: Adam <2363879+adamdotdevin@users.noreply.github.com> Date: Tue, 28 Oct 2025 12:15:46 -0500 Subject: wip: desktop work --- packages/ui/src/components/diff.css | 24 +++++ packages/ui/src/components/diff.tsx | 172 ++++++++++++++++++++++++++++++++++++ packages/ui/src/components/index.ts | 1 + 3 files changed, 197 insertions(+) create mode 100644 packages/ui/src/components/diff.css create mode 100644 packages/ui/src/components/diff.tsx (limited to 'packages/ui/src/components') diff --git a/packages/ui/src/components/diff.css b/packages/ui/src/components/diff.css new file mode 100644 index 000000000..6f9a0d73d --- /dev/null +++ b/packages/ui/src/components/diff.css @@ -0,0 +1,24 @@ +[data-component="diff"] { + [data-slot="diff-hunk-separator-line-number"] { + position: sticky; + left: 0; + background-color: hsla(209, 96%, 90%, 1); + z-index: 2; + display: flex; + align-items: center; + justify-content: center; + + [data-slot="diff-hunk-separator-line-number-icon"] { + aspect-ratio: 1; + width: 24px; + height: 24px; + } + } + [data-slot="diff-hunk-separator-content"] { + position: sticky; + background-color: hsla(210, 100%, 96%, 1); + width: var(--pjs-column-content-width); + left: var(--pjs-column-number-width); + padding-left: 8px; + } +} diff --git a/packages/ui/src/components/diff.tsx b/packages/ui/src/components/diff.tsx new file mode 100644 index 000000000..88215bcb0 --- /dev/null +++ b/packages/ui/src/components/diff.tsx @@ -0,0 +1,172 @@ +import { + type FileContents, + FileDiff, + type DiffLineAnnotation, + type HunkData, + DiffFileRendererOptions, + // registerCustomTheme, +} from "@pierre/precision-diffs" +import { ComponentProps, createEffect, splitProps } from "solid-js" + +export type DiffProps = Omit, "themes"> & { + before: FileContents + after: FileContents + annotations?: DiffLineAnnotation[] + class?: string + classList?: ComponentProps<"div">["classList"] +} + +// registerCustomTheme("opencode", () => import("./theme.json")) + +// interface ThreadMetadata { +// threadId: string +// } + +export function Diff(props: DiffProps) { + let container!: HTMLDivElement + const [local, others] = splitProps(props, ["before", "after", "class", "classList", "annotations"]) + + // const lineAnnotations: DiffLineAnnotation[] = [ + // { + // side: "additions", + // // The line number specified for an annotation is the visual line number + // // you see in the number column of a diff + // lineNumber: 16, + // metadata: { threadId: "68b329da9893e34099c7d8ad5cb9c940" }, + // }, + // ] + + // If you ever want to update the options for an instance, simple call + // 'setOptions' with the new options. Bear in mind, this does NOT merge + // existing properties, it's a full replace + // instance.setOptions({ + // ...instance.options, + // theme: "pierre-dark", + // themes: undefined, + // }) + + // When ready to render, simply call .render with old/new file, optional + // annotations and a container element to hold the diff + createEffect(() => { + const instance = new FileDiff({ + theme: "pierre-light", + // Or can also provide a 'themes' prop, which allows the code to adapt + // to your OS light or dark theme + // themes: { dark: 'pierre-night', light: 'pierre-light' }, + // When using the 'themes' prop, 'themeType' allows you to force 'dark' + // or 'light' theme, or inherit from the OS ('system') theme. + themeType: "system", + // Disable the line numbers for your diffs, generally not recommended + disableLineNumbers: false, + // Whether code should 'wrap' with long lines or 'scroll'. + overflow: "scroll", + // Normally you shouldn't need this prop, but if you don't provide a + // valid filename or your file doesn't have an extension you may want to + // override the automatic detection. You can specify that language here: + // https://shiki.style/languages + // lang?: SupportedLanguages; + // 'diffStyle' controls whether the diff is presented side by side or + // in a unified (single column) view + diffStyle: "unified", + // Line decorators to help highlight changes. + // 'bars' (default): + // Shows some red-ish or green-ish (theme dependent) bars on the left + // edge of relevant lines + // + // 'classic': + // shows '+' characters on additions and '-' characters on deletions + // + // 'none': + // No special diff indicators are shown + diffIndicators: "bars", + // By default green-ish or red-ish background are shown on added and + // deleted lines respectively. Disable that feature here + disableBackground: false, + // Diffs are split up into hunks, this setting customizes what to show + // between each hunk. + // + // 'line-info' (default): + // Shows a bar that tells you how many lines are collapsed. If you are + // using the oldFile/newFile API then you can click those bars to + // expand the content between them + // + // 'metadata': + // Shows the content you'd see in a normal patch file, usually in some + // format like '@@ -60,6 +60,22 @@'. You cannot use these to expand + // hidden content + // + // 'simple': + // Just a subtle bar separator between each hunk + // hunkSeparators: "line-info", + hunkSeparators(hunkData: HunkData) { + const fragment = document.createDocumentFragment() + const numCol = document.createElement("div") + numCol.innerHTML = ` ` + numCol.dataset["slot"] = "diff-hunk-separator-line-number" + fragment.appendChild(numCol) + const contentCol = document.createElement("div") + contentCol.textContent = `${hunkData.lines} unmodified lines` + contentCol.dataset["slot"] = "diff-hunk-separator-content" + fragment.appendChild(contentCol) + return fragment + }, + // On lines that have both additions and deletions, we can run a + // separate diff check to mark parts of the lines that change. + // 'none': + // Do not show these secondary highlights + // + // 'char': + // Show changes at a per character granularity + // + // 'word': + // Show changes but rounded up to word boundaries + // + // 'word-alt' (default): + // Similar to 'word', however we attempt to minimize single character + // gaps between highlighted changes + lineDiffType: "word-alt", + // If lines exceed these character lengths then we won't perform the + // line lineDiffType check + maxLineDiffLength: 1000, + // If any line in the diff exceeds this value then we won't attempt to + // syntax highlight the diff + maxLineLengthForHighlighting: 1000, + // Enabling this property will hide the file header with file name and + // diff stats. + disableFileHeader: true, + // You can optionally pass a render function for rendering out line + // annotations. Just return the dom node to render + // renderAnnotation(annotation: DiffLineAnnotation): HTMLElement { + // // Despite the diff itself being rendered in the shadow dom, + // // annotations are inserted via the web components 'slots' api and you + // // can use all your normal normal css and styling for them + // const element = document.createElement("div") + // element.innerText = annotation.metadata.threadId + // return element + // }, + ...others, + }) + + instance.render({ + oldFile: local.before, + newFile: local.after, + lineAnnotations: local.annotations, + containerWrapper: container, + }) + }) + + return ( +
+ ) +} diff --git a/packages/ui/src/components/index.ts b/packages/ui/src/components/index.ts index 0024c9e76..16cbb7d95 100644 --- a/packages/ui/src/components/index.ts +++ b/packages/ui/src/components/index.ts @@ -3,6 +3,7 @@ export * from "./button" export * from "./checkbox" export * from "./collapsible" export * from "./dialog" +export * from "./diff" export * from "./icon" export * from "./icon-button" export * from "./input" -- cgit v1.2.3