summaryrefslogtreecommitdiffhomepage
path: root/packages/ui/src/context
diff options
context:
space:
mode:
authorAdam <[email protected]>2025-10-30 13:49:29 -0500
committerAdam <[email protected]>2025-10-30 13:49:29 -0500
commitdc6e54503cb400ea2533740c9a92d09c8a50d077 (patch)
tree7abad7c0275fe646395a2f4f44d90e5f4a48dbe0 /packages/ui/src/context
parent2a0b67d84f048207d20d952cafa10c430451dc70 (diff)
downloadopencode-dc6e54503cb400ea2533740c9a92d09c8a50d077.tar.gz
opencode-dc6e54503cb400ea2533740c9a92d09c8a50d077.zip
wip: desktop work
Diffstat (limited to 'packages/ui/src/context')
-rw-r--r--packages/ui/src/context/helper.tsx25
-rw-r--r--packages/ui/src/context/marked.tsx30
-rw-r--r--packages/ui/src/context/shiki.tsx577
3 files changed, 632 insertions, 0 deletions
diff --git a/packages/ui/src/context/helper.tsx b/packages/ui/src/context/helper.tsx
new file mode 100644
index 000000000..6be88e775
--- /dev/null
+++ b/packages/ui/src/context/helper.tsx
@@ -0,0 +1,25 @@
+import { createContext, Show, useContext, type ParentProps } from "solid-js"
+
+export function createSimpleContext<T, Props extends Record<string, any>>(input: {
+ name: string
+ init: ((input: Props) => T) | (() => T)
+}) {
+ const ctx = createContext<T>()
+
+ return {
+ provider: (props: ParentProps<Props>) => {
+ const init = input.init(props)
+ return (
+ // @ts-expect-error
+ <Show when={init.ready === undefined || init.ready === true}>
+ <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
+ },
+ }
+}
diff --git a/packages/ui/src/context/marked.tsx b/packages/ui/src/context/marked.tsx
new file mode 100644
index 000000000..18ce4280a
--- /dev/null
+++ b/packages/ui/src/context/marked.tsx
@@ -0,0 +1,30 @@
+import { marked } from "marked"
+import markedShiki from "marked-shiki"
+import { bundledLanguages, type BundledLanguage } from "shiki"
+
+import { createSimpleContext } from "./helper"
+import { useShiki } from "./shiki"
+
+export const { use: useMarked, provider: MarkedProvider } = createSimpleContext({
+ name: "Marked",
+ init: () => {
+ const highlighter = useShiki()
+ return marked.use(
+ markedShiki({
+ async highlight(code, lang) {
+ 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,
+ })
+ },
+ }),
+ )
+ },
+})
diff --git a/packages/ui/src/context/shiki.tsx b/packages/ui/src/context/shiki.tsx
new file mode 100644
index 000000000..d33b98ab7
--- /dev/null
+++ b/packages/ui/src/context/shiki.tsx
@@ -0,0 +1,577 @@
+import { createSimpleContext } from "./helper"
+import { createHighlighter, type ThemeInput } from "shiki"
+
+const theme: ThemeInput = {
+ colors: {
+ "actionBar.toggledBackground": "var(--surface-raised-base)",
+ "activityBarBadge.background": "var(--surface-brand-base)",
+ "checkbox.border": "var(--border-base)",
+ "editor.background": "transparent",
+ "editor.foreground": "var(--text-base)",
+ "editor.inactiveSelectionBackground": "var(--surface-raised-base)",
+ "editor.selectionHighlightBackground": "var(--border-active)",
+ "editorIndentGuide.activeBackground1": "var(--border-weak-base)",
+ "editorIndentGuide.background1": "var(--border-weak-base)",
+ "input.placeholderForeground": "var(--text-weak)",
+ "list.activeSelectionIconForeground": "var(--text-base)",
+ "list.dropBackground": "var(--surface-raised-base)",
+ "menu.background": "var(--surface-base)",
+ "menu.border": "var(--border-base)",
+ "menu.foreground": "var(--text-base)",
+ "menu.selectionBackground": "var(--surface-interactive-base)",
+ "menu.separatorBackground": "var(--border-base)",
+ "ports.iconRunningProcessForeground": "var(--icon-success-base)",
+ "sideBarSectionHeader.background": "transparent",
+ "sideBarSectionHeader.border": "var(--border-weak-base)",
+ "sideBarTitle.foreground": "var(--text-weak)",
+ "statusBarItem.remoteBackground": "var(--surface-success-base)",
+ "statusBarItem.remoteForeground": "var(--text-base)",
+ "tab.lastPinnedBorder": "var(--border-weak-base)",
+ "tab.selectedBackground": "var(--surface-raised-base)",
+ "tab.selectedForeground": "var(--text-weak)",
+ "terminal.inactiveSelectionBackground": "var(--surface-raised-base)",
+ "widget.border": "var(--border-base)",
+ },
+ displayName: "opencode",
+ name: "opencode",
+ semanticHighlighting: true,
+ semanticTokenColors: {
+ customLiteral: "var(--syntax-function)",
+ newOperator: "var(--syntax-operator)",
+ numberLiteral: "var(--syntax-number)",
+ stringLiteral: "var(--syntax-string)",
+ },
+ tokenColors: [
+ {
+ scope: [
+ "meta.embedded",
+ "source.groovy.embedded",
+ "string meta.image.inline.markdown",
+ "variable.legacy.builtin.python",
+ ],
+ settings: {
+ foreground: "var(--text-base)",
+ },
+ },
+ {
+ scope: "emphasis",
+ settings: {
+ fontStyle: "italic",
+ },
+ },
+ {
+ scope: "strong",
+ settings: {
+ fontStyle: "bold",
+ },
+ },
+ {
+ scope: "header",
+ settings: {
+ foreground: "var(--markdown-heading)",
+ },
+ },
+ {
+ scope: "comment",
+ settings: {
+ foreground: "var(--syntax-comment)",
+ },
+ },
+ {
+ scope: "constant.language",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: [
+ "constant.numeric",
+ "variable.other.enummember",
+ "keyword.operator.plus.exponent",
+ "keyword.operator.minus.exponent",
+ ],
+ settings: {
+ foreground: "var(--syntax-number)",
+ },
+ },
+ {
+ scope: "constant.regexp",
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: "entity.name.tag",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: ["entity.name.tag.css", "entity.name.tag.less"],
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: "entity.other.attribute-name",
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: [
+ "entity.other.attribute-name.class.css",
+ "source.css entity.other.attribute-name.class",
+ "entity.other.attribute-name.id.css",
+ "entity.other.attribute-name.parent-selector.css",
+ "entity.other.attribute-name.parent.less",
+ "source.css entity.other.attribute-name.pseudo-class",
+ "entity.other.attribute-name.pseudo-element.css",
+ "source.css.less entity.other.attribute-name.id",
+ "entity.other.attribute-name.scss",
+ ],
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: "invalid",
+ settings: {
+ foreground: "var(--syntax-critical)",
+ },
+ },
+ {
+ scope: "markup.underline",
+ settings: {
+ fontStyle: "underline",
+ },
+ },
+ {
+ scope: "markup.bold",
+ settings: {
+ fontStyle: "bold",
+ foreground: "var(--markdown-strong)",
+ },
+ },
+ {
+ scope: "markup.heading",
+ settings: {
+ fontStyle: "bold",
+ foreground: "var(--theme-markdown-heading)",
+ },
+ },
+ {
+ scope: "markup.italic",
+ settings: {
+ fontStyle: "italic",
+ },
+ },
+ {
+ scope: "markup.strikethrough",
+ settings: {
+ fontStyle: "strikethrough",
+ },
+ },
+ {
+ scope: "markup.inserted",
+ settings: {
+ foreground: "var(--text-diff-add-base)",
+ },
+ },
+ {
+ scope: "markup.deleted",
+ settings: {
+ foreground: "var(--text-diff-delete-base)",
+ },
+ },
+ {
+ scope: "markup.changed",
+ settings: {
+ foreground: "var(--text-base)",
+ },
+ },
+ {
+ scope: "punctuation.definition.quote.begin.markdown",
+ settings: {
+ foreground: "var(--markdown-block-quote)",
+ },
+ },
+ {
+ scope: "punctuation.definition.list.begin.markdown",
+ settings: {
+ foreground: "var(--markdown-list-enumeration)",
+ },
+ },
+ {
+ scope: "markup.inline.raw",
+ settings: {
+ foreground: "var(--markdown-code)",
+ },
+ },
+ {
+ scope: "punctuation.definition.tag",
+ settings: {
+ foreground: "var(--syntax-punctuation)",
+ },
+ },
+ {
+ scope: ["meta.preprocessor", "entity.name.function.preprocessor"],
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "meta.preprocessor.string",
+ settings: {
+ foreground: "var(--syntax-string)",
+ },
+ },
+ {
+ scope: "meta.preprocessor.numeric",
+ settings: {
+ foreground: "var(--syntax-number)",
+ },
+ },
+ {
+ scope: "meta.structure.dictionary.key.python",
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: "meta.diff.header",
+ settings: {
+ foreground: "var(--text-weak)",
+ },
+ },
+ {
+ scope: "storage",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "storage.type",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: ["storage.modifier", "keyword.operator.noexcept"],
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: ["string", "meta.embedded.assembly"],
+ settings: {
+ foreground: "var(--syntax-string)",
+ },
+ },
+ {
+ scope: "string.tag",
+ settings: {
+ foreground: "var(--syntax-string)",
+ },
+ },
+ {
+ scope: "string.value",
+ settings: {
+ foreground: "var(--syntax-string)",
+ },
+ },
+ {
+ scope: "string.regexp",
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: [
+ "punctuation.definition.template-expression.begin",
+ "punctuation.definition.template-expression.end",
+ "punctuation.section.embedded",
+ ],
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: ["meta.template.expression"],
+ settings: {
+ foreground: "var(--text-base)",
+ },
+ },
+ {
+ scope: [
+ "support.type.vendored.property-name",
+ "support.type.property-name",
+ "source.css variable",
+ "source.coffee.embedded",
+ ],
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: "keyword",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "keyword.control",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "keyword.operator",
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: [
+ "keyword.operator.new",
+ "keyword.operator.expression",
+ "keyword.operator.cast",
+ "keyword.operator.sizeof",
+ "keyword.operator.alignof",
+ "keyword.operator.typeid",
+ "keyword.operator.alignas",
+ "keyword.operator.instanceof",
+ "keyword.operator.logical.python",
+ "keyword.operator.wordlike",
+ ],
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "keyword.other.unit",
+ settings: {
+ foreground: "var(--syntax-number)",
+ },
+ },
+ {
+ scope: ["punctuation.section.embedded.begin.php", "punctuation.section.embedded.end.php"],
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "support.function.git-rebase",
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: "constant.sha.git-rebase",
+ settings: {
+ foreground: "var(--syntax-number)",
+ },
+ },
+ {
+ scope: [
+ "storage.modifier.import.java",
+ "variable.language.wildcard.java",
+ "storage.modifier.package.java",
+ ],
+ settings: {
+ foreground: "var(--text-base)",
+ },
+ },
+ {
+ scope: "variable.language",
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: [
+ "entity.name.function",
+ "support.function",
+ "support.constant.handlebars",
+ "source.powershell variable.other.member",
+ "entity.name.operator.custom-literal",
+ ],
+ settings: {
+ foreground: "var(--syntax-function)",
+ },
+ },
+ {
+ scope: [
+ "support.class",
+ "support.type",
+ "entity.name.type",
+ "entity.name.namespace",
+ "entity.other.attribute",
+ "entity.name.scope-resolution",
+ "entity.name.class",
+ "storage.type.numeric.go",
+ "storage.type.byte.go",
+ "storage.type.boolean.go",
+ "storage.type.string.go",
+ "storage.type.uintptr.go",
+ "storage.type.error.go",
+ "storage.type.rune.go",
+ "storage.type.cs",
+ "storage.type.generic.cs",
+ "storage.type.modifier.cs",
+ "storage.type.variable.cs",
+ "storage.type.annotation.java",
+ "storage.type.generic.java",
+ "storage.type.java",
+ "storage.type.object.array.java",
+ "storage.type.primitive.array.java",
+ "storage.type.primitive.java",
+ "storage.type.token.java",
+ "storage.type.groovy",
+ "storage.type.annotation.groovy",
+ "storage.type.parameters.groovy",
+ "storage.type.generic.groovy",
+ "storage.type.object.array.groovy",
+ "storage.type.primitive.array.groovy",
+ "storage.type.primitive.groovy",
+ ],
+ settings: {
+ foreground: "var(--syntax-type)",
+ },
+ },
+ {
+ scope: [
+ "meta.type.cast.expr",
+ "meta.type.new.expr",
+ "support.constant.math",
+ "support.constant.dom",
+ "support.constant.json",
+ "entity.other.inherited-class",
+ "punctuation.separator.namespace.ruby",
+ ],
+ settings: {
+ foreground: "var(--syntax-type)",
+ },
+ },
+ {
+ scope: [
+ "keyword.control",
+ "source.cpp keyword.operator.new",
+ "keyword.operator.delete",
+ "keyword.other.using",
+ "keyword.other.directive.using",
+ "keyword.other.operator",
+ "entity.name.operator",
+ ],
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: [
+ "variable",
+ "meta.definition.variable.name",
+ "support.variable",
+ "entity.name.variable",
+ "constant.other.placeholder",
+ ],
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: ["variable.other.constant", "variable.other.enummember"],
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: ["meta.object-literal.key"],
+ settings: {
+ foreground: "var(--syntax-variable)",
+ },
+ },
+ {
+ scope: [
+ "support.constant.property-value",
+ "support.constant.font-name",
+ "support.constant.media-type",
+ "support.constant.media",
+ "constant.other.color.rgb-value",
+ "constant.other.rgb-value",
+ "support.constant.color",
+ ],
+ settings: {
+ foreground: "var(--syntax-string)",
+ },
+ },
+ {
+ scope: [
+ "punctuation.definition.group.regexp",
+ "punctuation.definition.group.assertion.regexp",
+ "punctuation.definition.character-class.regexp",
+ "punctuation.character.set.begin.regexp",
+ "punctuation.character.set.end.regexp",
+ "keyword.operator.negation.regexp",
+ "support.other.parenthesis.regexp",
+ ],
+ settings: {
+ foreground: "var(--syntax-string)",
+ },
+ },
+ {
+ scope: [
+ "constant.character.character-class.regexp",
+ "constant.other.character-class.set.regexp",
+ "constant.other.character-class.regexp",
+ "constant.character.set.regexp",
+ ],
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: ["keyword.operator.or.regexp", "keyword.control.anchor.regexp"],
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: "keyword.operator.quantifier.regexp",
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: ["constant.character", "constant.other.option"],
+ settings: {
+ foreground: "var(--syntax-keyword)",
+ },
+ },
+ {
+ scope: "constant.character.escape",
+ settings: {
+ foreground: "var(--syntax-operator)",
+ },
+ },
+ {
+ scope: "entity.name.label",
+ settings: {
+ foreground: "var(--text-weak)",
+ },
+ },
+ ],
+ type: "dark",
+}
+
+const highlighter = await createHighlighter({
+ themes: [theme],
+ langs: [],
+})
+
+export const { use: useShiki, provider: ShikiProvider } = createSimpleContext({
+ name: "Shiki",
+ init: () => {
+ return highlighter
+ },
+})