Vendor opencode source for docker build

This commit is contained in:
southseact-3d
2026-02-07 20:54:46 +00:00
parent b30ff1cfa4
commit efda260214
3195 changed files with 387717 additions and 1 deletions

View File

@@ -0,0 +1,10 @@
import type { ValidComponent } from "solid-js"
import { createSimpleContext } from "./helper"
const ctx = createSimpleContext<ValidComponent, { component: ValidComponent }>({
name: "CodeComponent",
init: (props) => props.component,
})
export const CodeComponentProvider = ctx.provider
export const useCodeComponent = ctx.use

View File

@@ -0,0 +1,74 @@
import type {
Message,
Session,
Part,
FileDiff,
SessionStatus,
PermissionRequest,
QuestionRequest,
QuestionAnswer,
} from "@opencode-ai/sdk/v2"
import { createSimpleContext } from "./helper"
import { PreloadMultiFileDiffResult } from "@pierre/diffs/ssr"
type Data = {
session: Session[]
session_status: {
[sessionID: string]: SessionStatus
}
session_diff: {
[sessionID: string]: FileDiff[]
}
session_diff_preload?: {
[sessionID: string]: PreloadMultiFileDiffResult<any>[]
}
permission?: {
[sessionID: string]: PermissionRequest[]
}
question?: {
[sessionID: string]: QuestionRequest[]
}
message: {
[sessionID: string]: Message[]
}
part: {
[messageID: string]: Part[]
}
}
export type PermissionRespondFn = (input: {
sessionID: string
permissionID: string
response: "once" | "always" | "reject"
}) => void
export type QuestionReplyFn = (input: { requestID: string; answers: QuestionAnswer[] }) => void
export type QuestionRejectFn = (input: { requestID: string }) => void
export type NavigateToSessionFn = (sessionID: string) => void
export const { use: useData, provider: DataProvider } = createSimpleContext({
name: "Data",
init: (props: {
data: Data
directory: string
onPermissionRespond?: PermissionRespondFn
onQuestionReply?: QuestionReplyFn
onQuestionReject?: QuestionRejectFn
onNavigateToSession?: NavigateToSessionFn
}) => {
return {
get store() {
return props.data
},
get directory() {
return props.directory
},
respondToPermission: props.onPermissionRespond,
replyToQuestion: props.onQuestionReply,
rejectQuestion: props.onQuestionReject,
navigateToSession: props.onNavigateToSession,
}
},
})

View File

@@ -0,0 +1,163 @@
import {
createContext,
createEffect,
createRoot,
createSignal,
getOwner,
onCleanup,
type Owner,
type ParentProps,
runWithOwner,
useContext,
type JSX,
} from "solid-js"
import { Dialog as Kobalte } from "@kobalte/core/dialog"
type DialogElement = () => JSX.Element
type Active = {
id: string
node: JSX.Element
dispose: () => void
owner: Owner
onClose?: () => void
setClosing: (closing: boolean) => void
}
const Context = createContext<ReturnType<typeof init>>()
function init() {
const [active, setActive] = createSignal<Active | undefined>()
const timer = { current: undefined as ReturnType<typeof setTimeout> | undefined }
const lock = { value: false }
onCleanup(() => {
if (timer.current === undefined) return
clearTimeout(timer.current)
timer.current = undefined
})
const close = () => {
const current = active()
if (!current || lock.value) return
lock.value = true
current.onClose?.()
current.setClosing(true)
const id = current.id
if (timer.current !== undefined) {
clearTimeout(timer.current)
timer.current = undefined
}
timer.current = setTimeout(() => {
timer.current = undefined
current.dispose()
if (active()?.id === id) setActive(undefined)
lock.value = false
}, 100)
}
createEffect(() => {
if (!active()) return
const onKeyDown = (event: KeyboardEvent) => {
if (event.key !== "Escape") return
close()
event.preventDefault()
event.stopPropagation()
}
window.addEventListener("keydown", onKeyDown, true)
onCleanup(() => window.removeEventListener("keydown", onKeyDown, true))
})
const show = (element: DialogElement, owner: Owner, onClose?: () => void) => {
// Immediately dispose any existing dialog when showing a new one
const current = active()
if (current) {
current.dispose()
setActive(undefined)
}
if (timer.current !== undefined) {
clearTimeout(timer.current)
timer.current = undefined
}
lock.value = false
const id = Math.random().toString(36).slice(2)
let dispose: (() => void) | undefined
let setClosing: ((closing: boolean) => void) | undefined
const node = runWithOwner(owner, () =>
createRoot((d: () => void) => {
dispose = d
const [closing, setClosingSignal] = createSignal(false)
setClosing = setClosingSignal
return (
<Kobalte
modal
open={!closing()}
onOpenChange={(open: boolean) => {
if (open) return
close()
}}
>
<Kobalte.Portal>
<Kobalte.Overlay data-component="dialog-overlay" onClick={close} />
{element()}
</Kobalte.Portal>
</Kobalte>
)
}),
)
if (!dispose || !setClosing) return
setActive({ id, node, dispose, owner, onClose, setClosing })
}
return {
get active() {
return active()
},
close,
show,
}
}
export function DialogProvider(props: ParentProps) {
const ctx = init()
return (
<Context.Provider value={ctx}>
{props.children}
<div data-component="dialog-stack">{ctx.active?.node}</div>
</Context.Provider>
)
}
export function useDialog() {
const ctx = useContext(Context)
const owner = getOwner()
if (!owner) {
throw new Error("useDialog must be used within a DialogProvider")
}
if (!ctx) {
throw new Error("useDialog must be used within a DialogProvider")
}
return {
get active() {
return ctx.active
},
show(element: DialogElement, onClose?: () => void) {
const base = ctx.active?.owner ?? owner
ctx.show(element, base, onClose)
},
close() {
ctx.close()
},
}
}

View File

@@ -0,0 +1,10 @@
import type { ValidComponent } from "solid-js"
import { createSimpleContext } from "./helper"
const ctx = createSimpleContext<ValidComponent, { component: ValidComponent }>({
name: "DiffComponent",
init: (props) => props.component,
})
export const DiffComponentProvider = ctx.provider
export const useDiffComponent = ctx.use

View File

@@ -0,0 +1,37 @@
import { createContext, createMemo, Show, useContext, type ParentProps, type Accessor } from "solid-js"
export function createSimpleContext<T, Props extends Record<string, any>>(input: {
name: string
init: ((input: Props) => T) | (() => T)
gate?: boolean
}) {
const ctx = createContext<T>()
return {
provider: (props: ParentProps<Props>) => {
const init = input.init(props)
const gate = input.gate ?? true
if (!gate) {
return <ctx.Provider value={init}>{props.children}</ctx.Provider>
}
// Access init.ready inside the memo to make it reactive for getter properties
const isReady = createMemo(() => {
// @ts-expect-error
const ready = init.ready as Accessor<boolean> | boolean | undefined
return ready === undefined || (typeof ready === "function" ? ready() : ready)
})
return (
<Show when={isReady()}>
<ctx.Provider value={init}>{props.children}</ctx.Provider>
</Show>
)
},
use() {
const value = useContext(ctx)
if (!value) throw new Error(`${input.name} context must be used within a context provider`)
return value
},
}
}

View File

@@ -0,0 +1,38 @@
import { createContext, useContext, type Accessor, type ParentProps } from "solid-js"
import { dict as en } from "../i18n/en"
export type UiI18nKey = keyof typeof en
export type UiI18nParams = Record<string, string | number | boolean>
export type UiI18n = {
locale: Accessor<string>
t: (key: UiI18nKey, params?: UiI18nParams) => string
}
function resolveTemplate(text: string, params?: UiI18nParams) {
if (!params) return text
return text.replace(/{{\s*([^}]+?)\s*}}/g, (_, rawKey) => {
const key = String(rawKey)
const value = params[key]
return value === undefined ? "" : String(value)
})
}
const fallback: UiI18n = {
locale: () => "en",
t: (key, params) => {
const value = en[key] ?? String(key)
return resolveTemplate(value, params)
},
}
const Context = createContext<UiI18n>(fallback)
export function I18nProvider(props: ParentProps<{ value: UiI18n }>) {
return <Context.Provider value={props.value}>{props.children}</Context.Provider>
}
export function useI18n() {
return useContext(Context)
}

View File

@@ -0,0 +1,5 @@
export * from "./helper"
export * from "./data"
export * from "./diff"
export * from "./dialog"
export * from "./i18n"

View File

@@ -0,0 +1,511 @@
import { marked } from "marked"
import markedKatex from "marked-katex-extension"
import markedShiki from "marked-shiki"
import katex from "katex"
import { bundledLanguages, type BundledLanguage } from "shiki"
import { createSimpleContext } from "./helper"
import { getSharedHighlighter, registerCustomTheme, ThemeRegistrationResolved } from "@pierre/diffs"
registerCustomTheme("OpenCode", () => {
return Promise.resolve({
name: "OpenCode",
colors: {
"editor.background": "transparent",
"editor.foreground": "var(--text-base)",
"gitDecoration.addedResourceForeground": "var(--syntax-diff-add)",
"gitDecoration.deletedResourceForeground": "var(--syntax-diff-delete)",
// "gitDecoration.conflictingResourceForeground": "#ffca00",
// "gitDecoration.modifiedResourceForeground": "#1a76d4",
// "gitDecoration.untrackedResourceForeground": "#00cab1",
// "gitDecoration.ignoredResourceForeground": "#84848A",
// "terminal.titleForeground": "#adadb1",
// "terminal.titleInactiveForeground": "#84848A",
// "terminal.background": "#141415",
// "terminal.foreground": "#adadb1",
// "terminal.ansiBlack": "#141415",
// "terminal.ansiRed": "#ff2e3f",
// "terminal.ansiGreen": "#0dbe4e",
// "terminal.ansiYellow": "#ffca00",
// "terminal.ansiBlue": "#008cff",
// "terminal.ansiMagenta": "#c635e4",
// "terminal.ansiCyan": "#08c0ef",
// "terminal.ansiWhite": "#c6c6c8",
// "terminal.ansiBrightBlack": "#141415",
// "terminal.ansiBrightRed": "#ff2e3f",
// "terminal.ansiBrightGreen": "#0dbe4e",
// "terminal.ansiBrightYellow": "#ffca00",
// "terminal.ansiBrightBlue": "#008cff",
// "terminal.ansiBrightMagenta": "#c635e4",
// "terminal.ansiBrightCyan": "#08c0ef",
// "terminal.ansiBrightWhite": "#c6c6c8",
},
tokenColors: [
{
scope: ["comment", "punctuation.definition.comment", "string.comment"],
settings: {
foreground: "var(--syntax-comment)",
},
},
{
scope: ["entity.other.attribute-name"],
settings: {
foreground: "var(--syntax-property)", // maybe attribute
},
},
{
scope: ["constant", "entity.name.constant", "variable.other.constant", "variable.language", "entity"],
settings: {
foreground: "var(--syntax-constant)",
},
},
{
scope: ["entity.name", "meta.export.default", "meta.definition.variable"],
settings: {
foreground: "var(--syntax-type)",
},
},
{
scope: ["meta.object.member"],
settings: {
foreground: "var(--syntax-primitive)",
},
},
{
scope: [
"variable.parameter.function",
"meta.jsx.children",
"meta.block",
"meta.tag.attributes",
"entity.name.constant",
"meta.embedded.expression",
"meta.template.expression",
"string.other.begin.yaml",
"string.other.end.yaml",
],
settings: {
foreground: "var(--syntax-punctuation)",
},
},
{
scope: ["entity.name.function", "support.type.primitive"],
settings: {
foreground: "var(--syntax-primitive)",
},
},
{
scope: ["support.class.component"],
settings: {
foreground: "var(--syntax-type)",
},
},
{
scope: "keyword",
settings: {
foreground: "var(--syntax-keyword)",
},
},
{
scope: [
"keyword.operator",
"storage.type.function.arrow",
"punctuation.separator.key-value.css",
"entity.name.tag.yaml",
"punctuation.separator.key-value.mapping.yaml",
],
settings: {
foreground: "var(--syntax-operator)",
},
},
{
scope: ["storage", "storage.type"],
settings: {
foreground: "var(--syntax-keyword)",
},
},
{
scope: ["storage.modifier.package", "storage.modifier.import", "storage.type.java"],
settings: {
foreground: "var(--syntax-primitive)",
},
},
{
scope: [
"string",
"punctuation.definition.string",
"string punctuation.section.embedded source",
"entity.name.tag",
],
settings: {
foreground: "var(--syntax-string)",
},
},
{
scope: "support",
settings: {
foreground: "var(--syntax-primitive)",
},
},
{
scope: ["support.type.object.module", "variable.other.object", "support.type.property-name.css"],
settings: {
foreground: "var(--syntax-object)",
},
},
{
scope: "meta.property-name",
settings: {
foreground: "var(--syntax-property)",
},
},
{
scope: "variable",
settings: {
foreground: "var(--syntax-variable)",
},
},
{
scope: "variable.other",
settings: {
foreground: "var(--syntax-variable)",
},
},
{
scope: [
"invalid.broken",
"invalid.illegal",
"invalid.unimplemented",
"invalid.deprecated",
"message.error",
"markup.deleted",
"meta.diff.header.from-file",
"punctuation.definition.deleted",
"brackethighlighter.unmatched",
"token.error-token",
],
settings: {
foreground: "var(--syntax-critical)",
},
},
{
scope: "carriage-return",
settings: {
foreground: "var(--syntax-keyword)",
},
},
{
scope: "string source",
settings: {
foreground: "var(--syntax-variable)",
},
},
{
scope: "string variable",
settings: {
foreground: "var(--syntax-constant)",
},
},
{
scope: [
"source.regexp",
"string.regexp",
"string.regexp.character-class",
"string.regexp constant.character.escape",
"string.regexp source.ruby.embedded",
"string.regexp string.regexp.arbitrary-repitition",
"string.regexp constant.character.escape",
],
settings: {
foreground: "var(--syntax-regexp)",
},
},
{
scope: "support.constant",
settings: {
foreground: "var(--syntax-primitive)",
},
},
{
scope: "support.variable",
settings: {
foreground: "var(--syntax-variable)",
},
},
{
scope: "meta.module-reference",
settings: {
foreground: "var(--syntax-info)",
},
},
{
scope: "punctuation.definition.list.begin.markdown",
settings: {
foreground: "var(--syntax-punctuation)",
},
},
{
scope: ["markup.heading", "markup.heading entity.name"],
settings: {
fontStyle: "bold",
foreground: "var(--syntax-info)",
},
},
{
scope: "markup.quote",
settings: {
foreground: "var(--syntax-info)",
},
},
{
scope: "markup.italic",
settings: {
fontStyle: "italic",
// foreground: "",
},
},
{
scope: "markup.bold",
settings: {
fontStyle: "bold",
foreground: "var(--text-strong)",
},
},
{
scope: [
"markup.raw",
"markup.inserted",
"meta.diff.header.to-file",
"punctuation.definition.inserted",
"markup.changed",
"punctuation.definition.changed",
"markup.ignored",
"markup.untracked",
],
settings: {
foreground: "var(--text-base)",
},
},
{
scope: "meta.diff.range",
settings: {
fontStyle: "bold",
foreground: "var(--syntax-unknown)",
},
},
{
scope: "meta.diff.header",
settings: {
foreground: "var(--syntax-unknown)",
},
},
{
scope: "meta.separator",
settings: {
fontStyle: "bold",
foreground: "var(--syntax-unknown)",
},
},
{
scope: "meta.output",
settings: {
foreground: "var(--syntax-unknown)",
},
},
{
scope: "meta.export.default",
settings: {
foreground: "var(--syntax-unknown)",
},
},
{
scope: [
"brackethighlighter.tag",
"brackethighlighter.curly",
"brackethighlighter.round",
"brackethighlighter.square",
"brackethighlighter.angle",
"brackethighlighter.quote",
],
settings: {
foreground: "var(--syntax-unknown)",
},
},
{
scope: ["constant.other.reference.link", "string.other.link"],
settings: {
fontStyle: "underline",
foreground: "var(--syntax-unknown)",
},
},
{
scope: "token.info-token",
settings: {
foreground: "var(--syntax-info)",
},
},
{
scope: "token.warn-token",
settings: {
foreground: "var(--syntax-warning)",
},
},
{
scope: "token.debug-token",
settings: {
foreground: "var(--syntax-info)",
},
},
],
semanticTokenColors: {
comment: "var(--syntax-comment)",
string: "var(--syntax-string)",
number: "var(--syntax-constant)",
regexp: "var(--syntax-regexp)",
keyword: "var(--syntax-keyword)",
variable: "var(--syntax-variable)",
parameter: "var(--syntax-variable)",
property: "var(--syntax-property)",
function: "var(--syntax-primitive)",
method: "var(--syntax-primitive)",
type: "var(--syntax-type)",
class: "var(--syntax-type)",
namespace: "var(--syntax-type)",
enumMember: "var(--syntax-primitive)",
"variable.constant": "var(--syntax-constant)",
"variable.defaultLibrary": "var(--syntax-unknown)",
},
} as unknown as ThemeRegistrationResolved)
})
function renderMathInText(text: string): string {
let result = text
// Display math: $$...$$
const displayMathRegex = /\$\$([\s\S]*?)\$\$/g
result = result.replace(displayMathRegex, (_, math) => {
try {
return katex.renderToString(math, {
displayMode: true,
throwOnError: false,
})
} catch {
return `$$${math}$$`
}
})
// Inline math: $...$
const inlineMathRegex = /(?<!\$)\$(?!\$)((?:[^$\\]|\\.)+?)\$(?!\$)/g
result = result.replace(inlineMathRegex, (_, math) => {
try {
return katex.renderToString(math, {
displayMode: false,
throwOnError: false,
})
} catch {
return `$${math}$`
}
})
return result
}
function renderMathExpressions(html: string): string {
// Split on code/pre/kbd tags to avoid processing their contents
const codeBlockPattern = /(<(?:pre|code|kbd)[^>]*>[\s\S]*?<\/(?:pre|code|kbd)>)/gi
const parts = html.split(codeBlockPattern)
return parts
.map((part, i) => {
// Odd indices are the captured code blocks - leave them alone
if (i % 2 === 1) return part
// Process math only in non-code parts
return renderMathInText(part)
})
.join("")
}
async function highlightCodeBlocks(html: string): Promise<string> {
const codeBlockRegex = /<pre><code(?:\s+class="language-([^"]*)")?>([\s\S]*?)<\/code><\/pre>/g
const matches = [...html.matchAll(codeBlockRegex)]
if (matches.length === 0) return html
const highlighter = await getSharedHighlighter({ themes: ["OpenCode"], langs: [] })
let result = html
for (const match of matches) {
const [fullMatch, lang, escapedCode] = match
const code = escapedCode
.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&amp;/g, "&")
.replace(/&quot;/g, '"')
.replace(/&#39;/g, "'")
let language = lang || "text"
if (!(language in bundledLanguages)) {
language = "text"
}
if (!highlighter.getLoadedLanguages().includes(language)) {
await highlighter.loadLanguage(language as BundledLanguage)
}
const highlighted = highlighter.codeToHtml(code, {
lang: language,
theme: "OpenCode",
tabindex: false,
})
result = result.replace(fullMatch, () => highlighted)
}
return result
}
export type NativeMarkdownParser = (markdown: string) => Promise<string>
export const { use: useMarked, provider: MarkedProvider } = createSimpleContext({
name: "Marked",
init: (props: { nativeParser?: NativeMarkdownParser }) => {
const jsParser = marked.use(
{
renderer: {
link({ href, title, text }) {
const titleAttr = title ? ` title="${title}"` : ""
return `<a href="${href}"${titleAttr} class="external-link" target="_blank" rel="noopener noreferrer">${text}</a>`
},
},
},
markedKatex({
throwOnError: false,
nonStandard: true,
}),
markedShiki({
async highlight(code, lang) {
const highlighter = await getSharedHighlighter({ themes: ["OpenCode"], langs: [] })
if (!(lang in bundledLanguages)) {
lang = "text"
}
if (!highlighter.getLoadedLanguages().includes(lang)) {
await highlighter.loadLanguage(lang as BundledLanguage)
}
return highlighter.codeToHtml(code, {
lang: lang || "text",
theme: "OpenCode",
tabindex: false,
})
},
}),
)
if (props.nativeParser) {
const nativeParser = props.nativeParser
return {
async parse(markdown: string): Promise<string> {
const html = await nativeParser(markdown)
const withMath = renderMathExpressions(html)
return highlightCodeBlocks(withMath)
},
}
}
return jsParser
},
})

View File

@@ -0,0 +1,20 @@
import type { WorkerPoolManager } from "@pierre/diffs/worker"
import { createSimpleContext } from "./helper"
export type WorkerPools = {
unified: WorkerPoolManager | undefined
split: WorkerPoolManager | undefined
}
const ctx = createSimpleContext<WorkerPools, { pools: WorkerPools }>({
name: "WorkerPool",
init: (props) => props.pools,
})
export const WorkerPoolProvider = ctx.provider
export function useWorkerPool(diffStyle: "unified" | "split" | undefined) {
const pools = ctx.use()
if (diffStyle === "split") return pools.split
return pools.unified
}