summaryrefslogtreecommitdiffhomepage
path: root/packages/app/src/components/code.tsx
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-09-26 11:41:15 -0500
committerAdam <[email protected]>2025-10-02 08:34:01 -0500
commitcc955098cd8714bcf1cc91e6a4a6625e38710b05 (patch)
tree49adeef653a6705c37b2e06cde1b5066765eeed3 /packages/app/src/components/code.tsx
parent8699e896e604762d45df7d4e1b3433e69575e9ab (diff)
downloadopencode-cc955098cd8714bcf1cc91e6a4a6625e38710b05.tar.gz
opencode-cc955098cd8714bcf1cc91e6a4a6625e38710b05.zip
wip: desktop work
Diffstat (limited to 'packages/app/src/components/code.tsx')
-rw-r--r--packages/app/src/components/code.tsx73
1 files changed, 64 insertions, 9 deletions
diff --git a/packages/app/src/components/code.tsx b/packages/app/src/components/code.tsx
index 63f527c46..40a40aa9a 100644
--- a/packages/app/src/components/code.tsx
+++ b/packages/app/src/components/code.tsx
@@ -1,8 +1,11 @@
import { bundledLanguages, type BundledLanguage, type ShikiTransformer } from "shiki"
import { splitProps, type ComponentProps, createEffect, onMount, onCleanup, createMemo, createResource } from "solid-js"
import { useLocal, useShiki } from "@/context"
+import type { TextSelection } from "@/context/local"
import { getFileExtension, getNodeOffsetInLine, getSelectionInContainer } from "@/utils"
+type DefinedSelection = Exclude<TextSelection, undefined>
+
interface Props extends ComponentProps<"div"> {
code: string
path: string
@@ -21,17 +24,66 @@ export function Code(props: Props) {
let container: HTMLDivElement | undefined
let isProgrammaticSelection = false
- const [html] = createResource(async () => {
- if (!highlighter.getLoadedLanguages().includes(lang())) {
- await highlighter.loadLanguage(lang() as BundledLanguage)
+ const ranges = createMemo<DefinedSelection[]>(() => {
+ const items = ctx.context.all() as Array<{ type: "file"; path: string; selection?: DefinedSelection }>
+ const result: DefinedSelection[] = []
+ for (const item of items) {
+ if (item.path !== local.path) continue
+ const selection = item.selection
+ if (!selection) continue
+ result.push(selection)
}
- return highlighter.codeToHtml(local.code || "", {
- lang: lang() && lang() in bundledLanguages ? lang() : "text",
- theme: "opencode",
- transformers: [transformerUnifiedDiff(), transformerDiffGroups()],
- }) as string
+ return result
})
+ const createLineNumberTransformer = (selections: DefinedSelection[]): ShikiTransformer => {
+ const highlighted = new Set<number>()
+ for (const selection of selections) {
+ const startLine = selection.startLine
+ const endLine = selection.endLine
+ const start = Math.max(1, Math.min(startLine, endLine))
+ const end = Math.max(start, Math.max(startLine, endLine))
+ const count = end - start + 1
+ if (count <= 0) continue
+ const values = Array.from({ length: count }, (_, index) => start + index)
+ for (const value of values) highlighted.add(value)
+ }
+ return {
+ name: "line-number-highlight",
+ line(node, index) {
+ if (!highlighted.has(index)) return
+ this.addClassToHast(node, "line-number-highlight")
+ const children = node.children
+ if (!Array.isArray(children)) return
+ for (const child of children) {
+ if (!child || typeof child !== "object") continue
+ const element = child as { type?: string; properties?: { className?: string[] } }
+ if (element.type !== "element") continue
+ const className = element.properties?.className
+ if (!Array.isArray(className)) continue
+ const matches = className.includes("diff-oldln") || className.includes("diff-newln")
+ if (!matches) continue
+ if (className.includes("line-number-highlight")) continue
+ className.push("line-number-highlight")
+ }
+ },
+ }
+ }
+
+ const [html] = createResource(
+ () => ranges(),
+ async (activeRanges) => {
+ if (!highlighter.getLoadedLanguages().includes(lang())) {
+ await highlighter.loadLanguage(lang() as BundledLanguage)
+ }
+ return highlighter.codeToHtml(local.code || "", {
+ lang: lang() && lang() in bundledLanguages ? lang() : "text",
+ theme: "opencode",
+ transformers: [transformerUnifiedDiff(), transformerDiffGroups(), createLineNumberTransformer(activeRanges)],
+ }) as string
+ },
+ )
+
onMount(() => {
if (!container) return
@@ -283,7 +335,7 @@ export function Code(props: Props) {
[&]:[counter-reset:line]
[&_pre]:focus-visible:outline-none
[&_pre]:overflow-x-auto [&_pre]:no-scrollbar
- [&_code]:min-w-full [&_code]:inline-block [&_code]:pb-40
+ [&_code]:min-w-full [&_code]:inline-block
[&_.tab]:relative
[&_.tab::before]:content['⇥']
[&_.tab::before]:absolute
@@ -303,6 +355,9 @@ export function Code(props: Props) {
[&_.line::before]:select-none
[&_.line::before]:[counter-increment:line]
[&_.line::before]:content-[counter(line)]
+ [&_.line-number-highlight]:bg-accent/20
+ [&_.line-number-highlight::before]:bg-accent/40!
+ [&_.line-number-highlight::before]:text-background-panel!
[&_code.code-diff_.line::before]:content-['']
[&_code.code-diff_.line::before]:w-0
[&_code.code-diff_.line::before]:pr-0