From e950ad5306944fe1897949dee9573526206a6860 Mon Sep 17 00:00:00 2001
From: Jeremy Osih
Date: Fri, 27 Jun 2025 00:38:14 +0200
Subject: feat(web): add scroll to last message button
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add intelligent floating scroll button for long conversations that:
- Only appears when scrolling down (direction-aware)
- Auto-hides after 3 seconds of inactivity
- Stays visible on hover to prevent accidental disappearance
- Uses consistent design patterns with repo styling
- Includes proper accessibility features
🤖 Generated with [opencode](https://opencode.ai)
Co-Authored-By: Jeremy Osih
Co-Authored-By: opencode
---
packages/web/src/components/Share.tsx | 94 ++++++++++++++++++++++++++++
packages/web/src/components/share.module.css | 35 +++++++++++
2 files changed, 129 insertions(+)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index 07b12946b..5510f5a69 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -40,6 +40,7 @@ import {
IconMagnifyingGlass,
IconWrenchScrewdriver,
IconDocumentMagnifyingGlass,
+ IconArrowDown,
} from "./icons"
import DiffView from "./DiffView"
import CodeBlock from "./CodeBlock"
@@ -721,6 +722,83 @@ export default function Share(props: {
})
})
+ const [showScrollButton, setShowScrollButton] = createSignal(false)
+ const [isButtonHovered, setIsButtonHovered] = createSignal(false)
+ let scrollTimeout: number | undefined
+ let lastScrollY = 0
+
+ const checkScrollNeed = () => {
+ const currentScrollY = window.scrollY
+ const isScrollingDown = currentScrollY > lastScrollY
+ const scrolled = currentScrollY > 200 // Show after scrolling 200px
+ const isNearBottom = window.innerHeight + currentScrollY >= document.body.scrollHeight - 100
+
+ // Only show when scrolling down, scrolled enough, and not near bottom
+ const shouldShow = isScrollingDown && scrolled && !isNearBottom
+
+ // Update last scroll position
+ lastScrollY = currentScrollY
+
+ if (shouldShow) {
+ setShowScrollButton(true)
+ // Clear existing timeout
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ // Hide button after 3 seconds of no scrolling (unless hovered)
+ scrollTimeout = window.setTimeout(() => {
+ if (!isButtonHovered()) {
+ setShowScrollButton(false)
+ }
+ }, 3000)
+ } else if (!isButtonHovered()) {
+ // Only hide if not hovered (to prevent disappearing while user is about to click)
+ setShowScrollButton(false)
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ }
+ }
+
+ const handleButtonMouseEnter = () => {
+ setIsButtonHovered(true)
+ // Clear timeout when hovering
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ }
+
+ const handleButtonMouseLeave = () => {
+ setIsButtonHovered(false)
+ // Restart timeout when leaving hover
+ if (showScrollButton()) {
+ scrollTimeout = window.setTimeout(() => {
+ if (!isButtonHovered()) {
+ setShowScrollButton(false)
+ }
+ }, 3000)
+ }
+ }
+
+ const scrollToBottom = () => {
+ document.body.scrollIntoView({ behavior: "smooth", block: "end" })
+ }
+
+ onMount(() => {
+ lastScrollY = window.scrollY // Initialize scroll position
+ checkScrollNeed()
+ window.addEventListener("scroll", checkScrollNeed)
+ window.addEventListener("resize", checkScrollNeed)
+ })
+
+ onCleanup(() => {
+ window.removeEventListener("scroll", checkScrollNeed)
+ window.removeEventListener("resize", checkScrollNeed)
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ })
+
const data = createMemo(() => {
const result = {
rootDir: undefined as string | undefined,
@@ -875,6 +953,7 @@ export default function Share(props: {
)}
+
@@ -1975,6 +2054,21 @@ export default function Share(props: {
+
+ {/* Floating scroll to bottom button */}
+
+
+
)
}
diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css
index 53f082c9b..a52fd1765 100644
--- a/packages/web/src/components/share.module.css
+++ b/packages/web/src/components/share.module.css
@@ -760,3 +760,38 @@
}
}
}
+
+.scrollButton {
+ position: fixed;
+ bottom: 2rem;
+ right: 2rem;
+ width: 2.5rem;
+ height: 2.5rem;
+ border-radius: 0.25rem;
+ border: 1px solid var(--sl-color-divider);
+ background-color: var(--sl-color-bg-surface);
+ color: var(--sl-color-text-secondary);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
+ transition: all 0.15s ease, opacity 0.5s ease;
+ z-index: 100;
+ appearance: none;
+ opacity: 1;
+
+ &:hover {
+ color: var(--sl-color-text);
+ border-color: var(--sl-color-hairline);
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
+ }
+
+ &:active {
+ transform: translateY(1px);
+ }
+
+ svg {
+ display: block;
+ }
+}
--
cgit v1.2.3
From 065f0aaddf6612aa30e6977aeb9afa2e3a774c56 Mon Sep 17 00:00:00 2001
From: Jay V
Date: Thu, 26 Jun 2025 19:02:42 -0400
Subject: docs: tweak lander
---
packages/web/src/components/Lander.astro | 67 ++++++++++++++++++++++----------
1 file changed, 47 insertions(+), 20 deletions(-)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Lander.astro b/packages/web/src/components/Lander.astro
index ae1b2a5e9..8d4d4efed 100644
--- a/packages/web/src/components/Lander.astro
+++ b/packages/web/src/components/Lander.astro
@@ -19,8 +19,10 @@ const imageAttrs = {
const github = config.social.filter(s => s.icon === 'github')[0];
-const command = "npm i -g";
-const pkg = "opencode-ai";
+const command = "curl -fsSL"
+const protocol = "https://"
+const url = "opencode.ai/install"
+const bash = "| bash"
let darkImage: ImageMetadata | undefined;
let lightImage: ImageMetadata | undefined;
@@ -51,11 +53,14 @@ if (image) {
-
@@ -133,39 +138,43 @@ section.top {
section.cta {
display: flex;
flex-direction: row;
- justify-content: space-between;
+ justify-content: flex-start;
+ align-items: stretch;
border-top: 2px solid var(--sl-color-border);
- @media (max-width: 40rem) {
+ @media (max-width: 50rem) {
flex-direction: column;
+
+ & > div.col1 { order: 1; }
+ & > div.col3 { order: 2; }
+ & > div.col2 { order: 3; }
}
& > div {
- flex: 1;
line-height: 1.4;
- padding: calc(var(--padding) / 2) 0.5rem;
-
- @media (max-width: 40rem) {
- padding-bottom: calc(var(--padding) / 2 + 4px);
- }
+ padding: calc(var(--padding) / 2) 1rem;
a {
font-size: 1rem;
}
}
- & > div.col2 {
+ & > div.col1, & > div.col3 {
+ flex: 1 1 auto;
+ text-align: center;
+ text-transform: uppercase;
+
@media (max-width: 50rem) {
- flex: 0 0 auto;
+ padding-bottom: calc(var(--padding) / 2 + 4px);
}
}
- & > div:not(.col2) {
- text-align: center;
- text-transform: uppercase;
+ & > div.col2 {
+ flex: 0 0 auto;
}
& > div + div {
border-left: 2px solid var(--sl-color-border);
- @media (max-width: 40rem) {
+
+ @media (max-width: 50rem) {
border-left: none;
border-top: 2px solid var(--sl-color-border);
}
@@ -183,6 +192,21 @@ section.cta {
code {
color: var(--sl-color-text-secondary);
font-size: 1.125rem;
+
+ @media (max-width: 24rem) {
+ font-size: 0.875rem;
+ }
+ @media (max-width: 30rem) {
+ span.protocol {
+ display: none;
+ }
+ }
+ @media (max-width: 43rem) {
+ text-align: center;
+ span:first-child {
+ display: block;
+ }
+ }
}
code .highlight {
color: var(--sl-color-text);
@@ -192,6 +216,9 @@ section.cta {
.copy {
line-height: 1;
padding: 0;
+ @media (max-width: 43rem) {
+ display: none;
+ }
}
.copy svg {
width: 1rem;
--
cgit v1.2.3
From ebcf11e574d0ebb056248e84f495789e1b211437 Mon Sep 17 00:00:00 2001
From: Jay V
Date: Thu, 26 Jun 2025 19:47:58 -0400
Subject: docs: lander tweak
---
packages/web/src/components/Lander.astro | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Lander.astro b/packages/web/src/components/Lander.astro
index 8d4d4efed..82c33f3b3 100644
--- a/packages/web/src/components/Lander.astro
+++ b/packages/web/src/components/Lander.astro
@@ -58,8 +58,7 @@ if (image) {
- {command}
- {protocol}{url} {bash}
+ {command} {protocol}{url} {bash}
--
cgit v1.2.3
From f3742ddbb845e78c3bef96a639e285e4c19e3135 Mon Sep 17 00:00:00 2001
From: Dax Raad
Date: Thu, 26 Jun 2025 22:30:44 -0400
Subject: ignore: run prettier
---
opencode.json | 6 +-
packages/function/sst-env.d.ts | 16 +-
packages/opencode/config.schema.json | 34 +-
packages/opencode/src/app/app.ts | 1 -
packages/opencode/src/cli/cmd/auth.ts | 16 +-
packages/opencode/src/cli/cmd/models.ts | 4 +-
packages/opencode/src/cli/cmd/scrap.ts | 11 +-
packages/opencode/src/format/index.ts | 110 +++----
packages/opencode/src/tool/edit.ts | 1 +
packages/opencode/src/util/lazy.ts | 1 -
packages/opencode/src/util/log.ts | 5 +-
packages/opencode/sst-env.d.ts | 2 +-
packages/opencode/test/tool/edit.test.ts | 8 +-
packages/tui/AGENTS.md | 2 +-
packages/tui/internal/theme/themes/ayu.json | 1 -
packages/tui/internal/theme/themes/catppuccin.json | 1 -
packages/tui/internal/theme/themes/cobalt2.json | 2 +-
packages/tui/internal/theme/themes/dracula.json | 2 +-
packages/tui/internal/theme/themes/everforest.json | 1 -
packages/tui/internal/theme/themes/github.json | 2 +-
packages/tui/internal/theme/themes/material.json | 2 +-
packages/tui/internal/theme/themes/monokai.json | 2 +-
packages/tui/internal/theme/themes/opencode.json | 1 -
packages/tui/internal/theme/themes/palenight.json | 2 +-
packages/tui/internal/theme/themes/rosepine.json | 2 +-
packages/tui/internal/theme/themes/solarized.json | 2 +-
.../tui/internal/theme/themes/synthwave84.json | 2 +-
packages/tui/internal/theme/themes/tokyonight.json | 1 -
packages/tui/internal/theme/themes/zenburn.json | 2 +-
packages/tui/pkg/client/gen/openapi.json | 344 ++++-----------------
packages/web/astro.config.mjs | 4 +-
31 files changed, 179 insertions(+), 411 deletions(-)
(limited to 'packages/web')
diff --git a/opencode.json b/opencode.json
index 59748f927..57b940082 100644
--- a/opencode.json
+++ b/opencode.json
@@ -3,7 +3,11 @@
"experimental": {
"hook": {
"file_edited": {
- ".json": []
+ ".json": [
+ {
+ "command": ["bun", "run", "prettier", "$FILE"]
+ }
+ ]
},
"session_completed": [
{
diff --git a/packages/function/sst-env.d.ts b/packages/function/sst-env.d.ts
index 78b7056c1..fd95edbb4 100644
--- a/packages/function/sst-env.d.ts
+++ b/packages/function/sst-env.d.ts
@@ -6,20 +6,20 @@
import "sst"
declare module "sst" {
export interface Resource {
- "Web": {
- "type": "sst.cloudflare.Astro"
- "url": string
+ Web: {
+ type: "sst.cloudflare.Astro"
+ url: string
}
}
}
-// cloudflare
-import * as cloudflare from "@cloudflare/workers-types";
+// cloudflare
+import * as cloudflare from "@cloudflare/workers-types"
declare module "sst" {
export interface Resource {
- "Api": cloudflare.Service
- "Bucket": cloudflare.R2Bucket
+ Api: cloudflare.Service
+ Bucket: cloudflare.R2Bucket
}
}
import "sst"
-export {}
\ No newline at end of file
+export {}
diff --git a/packages/opencode/config.schema.json b/packages/opencode/config.schema.json
index 98fadb65b..813b1225f 100644
--- a/packages/opencode/config.schema.json
+++ b/packages/opencode/config.schema.json
@@ -202,10 +202,7 @@
"type": "number"
}
},
- "required": [
- "input",
- "output"
- ],
+ "required": ["input", "output"],
"additionalProperties": false
},
"limit": {
@@ -218,10 +215,7 @@
"type": "number"
}
},
- "required": [
- "context",
- "output"
- ],
+ "required": ["context", "output"],
"additionalProperties": false
},
"id": {
@@ -240,9 +234,7 @@
"additionalProperties": {}
}
},
- "required": [
- "models"
- ],
+ "required": ["models"],
"additionalProperties": false
},
"description": "Custom provider configurations and model overrides"
@@ -274,10 +266,7 @@
"description": "Environment variables to set when running the MCP server"
}
},
- "required": [
- "type",
- "command"
- ],
+ "required": ["type", "command"],
"additionalProperties": false
},
{
@@ -293,10 +282,7 @@
"description": "URL of the remote MCP server"
}
},
- "required": [
- "type",
- "url"
- ],
+ "required": ["type", "url"],
"additionalProperties": false
}
]
@@ -329,9 +315,7 @@
}
}
},
- "required": [
- "command"
- ],
+ "required": ["command"],
"additionalProperties": false
}
}
@@ -354,9 +338,7 @@
}
}
},
- "required": [
- "command"
- ],
+ "required": ["command"],
"additionalProperties": false
}
}
@@ -369,4 +351,4 @@
},
"additionalProperties": false,
"$schema": "http://json-schema.org/draft-07/schema#"
-}
\ No newline at end of file
+}
diff --git a/packages/opencode/src/app/app.ts b/packages/opencode/src/app/app.ts
index 873554d90..b60702f9a 100644
--- a/packages/opencode/src/app/app.ts
+++ b/packages/opencode/src/app/app.ts
@@ -142,4 +142,3 @@ export namespace App {
.replace(/[^A-Za-z0-9_]/g, "-")
}
}
-
diff --git a/packages/opencode/src/cli/cmd/auth.ts b/packages/opencode/src/cli/cmd/auth.ts
index 05314d69b..9e8da95bd 100644
--- a/packages/opencode/src/cli/cmd/auth.ts
+++ b/packages/opencode/src/cli/cmd/auth.ts
@@ -31,7 +31,7 @@ export const AuthListCommand = cmd({
UI.empty()
const authPath = path.join(Global.Path.data, "auth.json")
const homedir = os.homedir()
- const displayPath = authPath.startsWith(homedir)
+ const displayPath = authPath.startsWith(homedir)
? authPath.replace(homedir, "~")
: authPath
prompts.intro(`Credentials ${UI.Style.TEXT_DIM}${displayPath}`)
@@ -46,14 +46,14 @@ export const AuthListCommand = cmd({
prompts.outro(`${results.length} credentials`)
// Environment variables section
- const activeEnvVars: Array<{ provider: string, envVar: string }> = []
-
+ const activeEnvVars: Array<{ provider: string; envVar: string }> = []
+
for (const [providerID, provider] of Object.entries(database)) {
for (const envVar of provider.env) {
if (process.env[envVar]) {
- activeEnvVars.push({
- provider: provider.name || providerID,
- envVar
+ activeEnvVars.push({
+ provider: provider.name || providerID,
+ envVar,
})
}
}
@@ -62,11 +62,11 @@ export const AuthListCommand = cmd({
if (activeEnvVars.length > 0) {
UI.empty()
prompts.intro("Environment")
-
+
for (const { provider, envVar } of activeEnvVars) {
prompts.log.info(`${provider} ${UI.Style.TEXT_DIM}${envVar}`)
}
-
+
prompts.outro(`${activeEnvVars.length} environment variables`)
}
},
diff --git a/packages/opencode/src/cli/cmd/models.ts b/packages/opencode/src/cli/cmd/models.ts
index 9380cec89..e385a310c 100644
--- a/packages/opencode/src/cli/cmd/models.ts
+++ b/packages/opencode/src/cli/cmd/models.ts
@@ -8,7 +8,7 @@ export const ModelsCommand = cmd({
handler: async () => {
await App.provide({ cwd: process.cwd() }, async () => {
const providers = await Provider.list()
-
+
for (const [providerID, provider] of Object.entries(providers)) {
for (const modelID of Object.keys(provider.info.models)) {
console.log(`${providerID}/${modelID}`)
@@ -16,4 +16,4 @@ export const ModelsCommand = cmd({
}
})
},
-})
\ No newline at end of file
+})
diff --git a/packages/opencode/src/cli/cmd/scrap.ts b/packages/opencode/src/cli/cmd/scrap.ts
index 03b69d4e8..20b0f52a3 100644
--- a/packages/opencode/src/cli/cmd/scrap.ts
+++ b/packages/opencode/src/cli/cmd/scrap.ts
@@ -7,12 +7,9 @@ export const ScrapCommand = cmd({
builder: (yargs) =>
yargs.positional("file", { type: "string", demandOption: true }),
async handler(args) {
- await App.provide(
- { cwd: process.cwd() },
- async () => {
- await LSP.touchFile(args.file, true)
- console.log(await LSP.diagnostics())
- },
- )
+ await App.provide({ cwd: process.cwd() }, async () => {
+ await LSP.touchFile(args.file, true)
+ console.log(await LSP.diagnostics())
+ })
},
})
diff --git a/packages/opencode/src/format/index.ts b/packages/opencode/src/format/index.ts
index 7aef384c1..0a873ce1f 100644
--- a/packages/opencode/src/format/index.ts
+++ b/packages/opencode/src/format/index.ts
@@ -1,13 +1,13 @@
-import { App } from '../app/app'
-import { BunProc } from '../bun'
-import { Config } from '../config/config'
-import { Log } from '../util/log'
-import path from 'path'
+import { App } from "../app/app"
+import { BunProc } from "../bun"
+import { Config } from "../config/config"
+import { Log } from "../util/log"
+import path from "path"
export namespace Format {
- const log = Log.create({ service: 'format' })
+ const log = Log.create({ service: "format" })
- const state = App.state('format', async () => {
+ const state = App.state("format", async () => {
const hooks: Record = {}
for (const item of FORMATTERS) {
if (await item.enabled()) {
@@ -42,22 +42,22 @@ export namespace Format {
})
export async function run(file: string) {
- log.info('formatting', { file })
+ log.info("formatting", { file })
const { hooks } = await state()
const ext = path.extname(file)
const match = hooks[ext]
if (!match) return
for (const item of match) {
- log.info('running', { command: item.command })
+ log.info("running", { command: item.command })
const proc = Bun.spawn({
- cmd: item.command.map((x) => x.replace('$FILE', file)),
+ cmd: item.command.map((x) => x.replace("$FILE", file)),
cwd: App.info().path.cwd,
env: item.environment,
})
const exit = await proc.exited
if (exit !== 0)
- log.error('failed', {
+ log.error("failed", {
command: item.command,
...item.environment,
})
@@ -79,58 +79,58 @@ export namespace Format {
const FORMATTERS: Native[] = [
{
- name: 'prettier',
- extensions: [
- '.js',
- '.jsx',
- '.mjs',
- '.cjs',
- '.ts',
- '.tsx',
- '.mts',
- '.cts',
- '.html',
- '.htm',
- '.css',
- '.scss',
- '.sass',
- '.less',
- '.vue',
- '.svelte',
- '.json',
- '.jsonc',
- '.yaml',
- '.yml',
- '.toml',
- '.xml',
- '.md',
- '.mdx',
- '.php',
- '.rb',
- '.java',
- '.go',
- '.rs',
- '.swift',
- '.kt',
- '.kts',
- '.sol',
- '.graphql',
- '.gql',
- ],
- command: [BunProc.which(), 'run', 'prettier', '--write', '$FILE'],
+ name: "prettier",
+ command: [BunProc.which(), "run", "prettier", "--write", "$FILE"],
environment: {
- BUN_BE_BUN: '1',
+ BUN_BE_BUN: "1",
},
+ extensions: [
+ ".js",
+ ".jsx",
+ ".mjs",
+ ".cjs",
+ ".ts",
+ ".tsx",
+ ".mts",
+ ".cts",
+ ".html",
+ ".htm",
+ ".css",
+ ".scss",
+ ".sass",
+ ".less",
+ ".vue",
+ ".svelte",
+ ".json",
+ ".jsonc",
+ ".yaml",
+ ".yml",
+ ".toml",
+ ".xml",
+ ".md",
+ ".mdx",
+ ".php",
+ ".rb",
+ ".java",
+ ".go",
+ ".rs",
+ ".swift",
+ ".kt",
+ ".kts",
+ ".sol",
+ ".graphql",
+ ".gql",
+ ],
async enabled() {
try {
const proc = Bun.spawn({
- cmd: [BunProc.which(), 'run', 'prettier', '--version'],
+ cmd: [BunProc.which(), "run", "prettier", "--version"],
cwd: App.info().path.cwd,
env: {
- BUN_BE_BUN: '1',
+ BUN_BE_BUN: "1",
},
- stdout: 'ignore',
- stderr: 'ignore',
+ stdout: "ignore",
+ stderr: "ignore",
})
const exit = await proc.exited
return exit === 0
diff --git a/packages/opencode/src/tool/edit.ts b/packages/opencode/src/tool/edit.ts
index f1e6535dc..d597635e2 100644
--- a/packages/opencode/src/tool/edit.ts
+++ b/packages/opencode/src/tool/edit.ts
@@ -80,6 +80,7 @@ export const EditTool = Tool.define({
)
await file.write(contentNew)
await Format.run(filepath)
+ contentNew = await file.text()
})()
const diff = trimDiff(
diff --git a/packages/opencode/src/util/lazy.ts b/packages/opencode/src/util/lazy.ts
index 8b7ded89f..3533bcc81 100644
--- a/packages/opencode/src/util/lazy.ts
+++ b/packages/opencode/src/util/lazy.ts
@@ -8,4 +8,3 @@ export function lazy(fn: () => T) {
return value as T
}
}
-
diff --git a/packages/opencode/src/util/log.ts b/packages/opencode/src/util/log.ts
index 7db81de0c..b73e2dee8 100644
--- a/packages/opencode/src/util/log.ts
+++ b/packages/opencode/src/util/log.ts
@@ -19,7 +19,10 @@ export namespace Log {
await fs.mkdir(dir, { recursive: true })
cleanup(dir)
if (options.print) return
- logpath = path.join(dir, new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log")
+ logpath = path.join(
+ dir,
+ new Date().toISOString().split(".")[0].replace(/:/g, "") + ".log",
+ )
const logfile = Bun.file(logpath)
await fs.truncate(logpath).catch(() => {})
const writer = logfile.writer()
diff --git a/packages/opencode/sst-env.d.ts b/packages/opencode/sst-env.d.ts
index b6a7e9066..0397645b5 100644
--- a/packages/opencode/sst-env.d.ts
+++ b/packages/opencode/sst-env.d.ts
@@ -6,4 +6,4 @@
///
import "sst"
-export {}
\ No newline at end of file
+export {}
diff --git a/packages/opencode/test/tool/edit.test.ts b/packages/opencode/test/tool/edit.test.ts
index f5558eaac..6de4f2a71 100644
--- a/packages/opencode/test/tool/edit.test.ts
+++ b/packages/opencode/test/tool/edit.test.ts
@@ -316,13 +316,13 @@ const testCases: TestCase[] = [
// WhitespaceNormalizedReplacer - test regex special characters that could cause errors
{
content: 'const pattern = "test[123]";',
- find: 'test[123]',
- replace: 'test[456]',
+ find: "test[123]",
+ replace: "test[456]",
},
{
content: 'const regex = "^start.*end$";',
- find: '^start.*end$',
- replace: '^begin.*finish$',
+ find: "^start.*end$",
+ replace: "^begin.*finish$",
},
// EscapeNormalizedReplacer - test single backslash vs double backslash
diff --git a/packages/tui/AGENTS.md b/packages/tui/AGENTS.md
index a757e926b..0000db9b8 100644
--- a/packages/tui/AGENTS.md
+++ b/packages/tui/AGENTS.md
@@ -23,4 +23,4 @@
- **Client**: Generated OpenAPI client communicates with TypeScript server
- **Components**: Reusable UI components in `internal/components/`
- **Themes**: JSON-based theming system with override hierarchy
-- **State**: Centralized app state with message passing
\ No newline at end of file
+- **State**: Centralized app state with message passing
diff --git a/packages/tui/internal/theme/themes/ayu.json b/packages/tui/internal/theme/themes/ayu.json
index 3ea7a5ca3..a42fce4c4 100644
--- a/packages/tui/internal/theme/themes/ayu.json
+++ b/packages/tui/internal/theme/themes/ayu.json
@@ -78,4 +78,3 @@
"syntaxPunctuation": "darkFg"
}
}
-
diff --git a/packages/tui/internal/theme/themes/catppuccin.json b/packages/tui/internal/theme/themes/catppuccin.json
index cc4d99f43..d0fa6a11d 100644
--- a/packages/tui/internal/theme/themes/catppuccin.json
+++ b/packages/tui/internal/theme/themes/catppuccin.json
@@ -110,4 +110,3 @@
"syntaxPunctuation": { "dark": "darkText", "light": "lightText" }
}
}
-
diff --git a/packages/tui/internal/theme/themes/cobalt2.json b/packages/tui/internal/theme/themes/cobalt2.json
index 5b9740e34..2967eae58 100644
--- a/packages/tui/internal/theme/themes/cobalt2.json
+++ b/packages/tui/internal/theme/themes/cobalt2.json
@@ -225,4 +225,4 @@
"light": "#193549"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/dracula.json b/packages/tui/internal/theme/themes/dracula.json
index d57692fbb..c837a0b58 100644
--- a/packages/tui/internal/theme/themes/dracula.json
+++ b/packages/tui/internal/theme/themes/dracula.json
@@ -216,4 +216,4 @@
"light": "#282a36"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/everforest.json b/packages/tui/internal/theme/themes/everforest.json
index 19cbb9aca..62dfb31ba 100644
--- a/packages/tui/internal/theme/themes/everforest.json
+++ b/packages/tui/internal/theme/themes/everforest.json
@@ -239,4 +239,3 @@
}
}
}
-
diff --git a/packages/tui/internal/theme/themes/github.json b/packages/tui/internal/theme/themes/github.json
index b27080101..99a80879e 100644
--- a/packages/tui/internal/theme/themes/github.json
+++ b/packages/tui/internal/theme/themes/github.json
@@ -230,4 +230,4 @@
"light": "lightFg"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/material.json b/packages/tui/internal/theme/themes/material.json
index bdb10a7f1..c3a106808 100644
--- a/packages/tui/internal/theme/themes/material.json
+++ b/packages/tui/internal/theme/themes/material.json
@@ -232,4 +232,4 @@
"light": "lightFg"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/monokai.json b/packages/tui/internal/theme/themes/monokai.json
index 39a5385e9..09637a1e2 100644
--- a/packages/tui/internal/theme/themes/monokai.json
+++ b/packages/tui/internal/theme/themes/monokai.json
@@ -218,4 +218,4 @@
"light": "#272822"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/opencode.json b/packages/tui/internal/theme/themes/opencode.json
index bdef5ab3e..8f585a450 100644
--- a/packages/tui/internal/theme/themes/opencode.json
+++ b/packages/tui/internal/theme/themes/opencode.json
@@ -243,4 +243,3 @@
}
}
}
-
diff --git a/packages/tui/internal/theme/themes/palenight.json b/packages/tui/internal/theme/themes/palenight.json
index c786b4c6e..79f7c59e8 100644
--- a/packages/tui/internal/theme/themes/palenight.json
+++ b/packages/tui/internal/theme/themes/palenight.json
@@ -219,4 +219,4 @@
"light": "#292d3e"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/rosepine.json b/packages/tui/internal/theme/themes/rosepine.json
index 612951d00..444cdbd13 100644
--- a/packages/tui/internal/theme/themes/rosepine.json
+++ b/packages/tui/internal/theme/themes/rosepine.json
@@ -231,4 +231,4 @@
"light": "dawnSubtle"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/solarized.json b/packages/tui/internal/theme/themes/solarized.json
index 2a8a3698c..e4de11367 100644
--- a/packages/tui/internal/theme/themes/solarized.json
+++ b/packages/tui/internal/theme/themes/solarized.json
@@ -220,4 +220,4 @@
"light": "base00"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/synthwave84.json b/packages/tui/internal/theme/themes/synthwave84.json
index a886eb942..d25bf3b49 100644
--- a/packages/tui/internal/theme/themes/synthwave84.json
+++ b/packages/tui/internal/theme/themes/synthwave84.json
@@ -223,4 +223,4 @@
"light": "#262335"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/internal/theme/themes/tokyonight.json b/packages/tui/internal/theme/themes/tokyonight.json
index cfd3c7ce6..1c9503a42 100644
--- a/packages/tui/internal/theme/themes/tokyonight.json
+++ b/packages/tui/internal/theme/themes/tokyonight.json
@@ -241,4 +241,3 @@
}
}
}
-
diff --git a/packages/tui/internal/theme/themes/zenburn.json b/packages/tui/internal/theme/themes/zenburn.json
index 04b338eb5..c4475923b 100644
--- a/packages/tui/internal/theme/themes/zenburn.json
+++ b/packages/tui/internal/theme/themes/zenburn.json
@@ -220,4 +220,4 @@
"light": "#3f3f3f"
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/tui/pkg/client/gen/openapi.json b/packages/tui/pkg/client/gen/openapi.json
index e804accf3..78d499e7f 100644
--- a/packages/tui/pkg/client/gen/openapi.json
+++ b/packages/tui/pkg/client/gen/openapi.json
@@ -115,11 +115,7 @@
"type": "string"
}
},
- "required": [
- "sessionID",
- "providerID",
- "modelID"
- ]
+ "required": ["sessionID", "providerID", "modelID"]
}
}
}
@@ -149,12 +145,7 @@
"type": "string"
}
},
- "required": [
- "root",
- "data",
- "cwd",
- "config"
- ]
+ "required": ["root", "data", "cwd", "config"]
}
}
}
@@ -221,9 +212,7 @@
"type": "string"
}
},
- "required": [
- "sessionID"
- ]
+ "required": ["sessionID"]
}
}
}
@@ -257,9 +246,7 @@
"type": "string"
}
},
- "required": [
- "sessionID"
- ]
+ "required": ["sessionID"]
}
}
}
@@ -296,9 +283,7 @@
"type": "string"
}
},
- "required": [
- "sessionID"
- ]
+ "required": ["sessionID"]
}
}
}
@@ -354,9 +339,7 @@
"type": "string"
}
},
- "required": [
- "sessionID"
- ]
+ "required": ["sessionID"]
}
}
}
@@ -390,9 +373,7 @@
"type": "string"
}
},
- "required": [
- "sessionID"
- ]
+ "required": ["sessionID"]
}
}
}
@@ -432,11 +413,7 @@
"type": "string"
}
},
- "required": [
- "sessionID",
- "providerID",
- "modelID"
- ]
+ "required": ["sessionID", "providerID", "modelID"]
}
}
}
@@ -482,12 +459,7 @@
}
}
},
- "required": [
- "sessionID",
- "providerID",
- "modelID",
- "parts"
- ]
+ "required": ["sessionID", "providerID", "modelID", "parts"]
}
}
}
@@ -517,10 +489,7 @@
}
}
},
- "required": [
- "providers",
- "default"
- ]
+ "required": ["providers", "default"]
}
}
}
@@ -561,9 +530,7 @@
"type": "string"
}
},
- "required": [
- "query"
- ]
+ "required": ["query"]
}
}
}
@@ -652,15 +619,10 @@
},
"content": {}
},
- "required": [
- "key"
- ]
+ "required": ["key"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"Event.installation.updated": {
"type": "object",
@@ -676,15 +638,10 @@
"type": "string"
}
},
- "required": [
- "version"
- ]
+ "required": ["version"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"Event.lsp.client.diagnostics": {
"type": "object",
@@ -703,16 +660,10 @@
"type": "string"
}
},
- "required": [
- "serverID",
- "path"
- ]
+ "required": ["serverID", "path"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"Event.permission.updated": {
"type": "object",
@@ -725,10 +676,7 @@
"$ref": "#/components/schemas/permission.info"
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"permission.info": {
"type": "object",
@@ -753,18 +701,10 @@
"type": "number"
}
},
- "required": [
- "created"
- ]
+ "required": ["created"]
}
},
- "required": [
- "id",
- "sessionID",
- "title",
- "metadata",
- "time"
- ]
+ "required": ["id", "sessionID", "title", "metadata", "time"]
},
"Event.message.updated": {
"type": "object",
@@ -780,15 +720,10 @@
"$ref": "#/components/schemas/Message.Info"
}
},
- "required": [
- "info"
- ]
+ "required": ["info"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"Message.Info": {
"type": "object",
@@ -798,10 +733,7 @@
},
"role": {
"type": "string",
- "enum": [
- "user",
- "assistant"
- ]
+ "enum": ["user", "assistant"]
},
"parts": {
"type": "array",
@@ -813,12 +745,7 @@
"$ref": "#/components/schemas/Message.Metadata"
}
},
- "required": [
- "id",
- "role",
- "parts",
- "metadata"
- ]
+ "required": ["id", "role", "parts", "metadata"]
},
"Message.Part": {
"oneOf": [
@@ -864,10 +791,7 @@
"type": "string"
}
},
- "required": [
- "type",
- "text"
- ]
+ "required": ["type", "text"]
},
"Message.Part.Reasoning": {
"type": "object",
@@ -884,10 +808,7 @@
"additionalProperties": {}
}
},
- "required": [
- "type",
- "text"
- ]
+ "required": ["type", "text"]
},
"Message.Part.ToolInvocation": {
"type": "object",
@@ -900,10 +821,7 @@
"$ref": "#/components/schemas/Message.ToolInvocation"
}
},
- "required": [
- "type",
- "toolInvocation"
- ]
+ "required": ["type", "toolInvocation"]
},
"Message.ToolInvocation": {
"oneOf": [
@@ -944,11 +862,7 @@
},
"args": {}
},
- "required": [
- "state",
- "toolCallId",
- "toolName"
- ]
+ "required": ["state", "toolCallId", "toolName"]
},
"Message.ToolInvocation.ToolPartialCall": {
"type": "object",
@@ -968,11 +882,7 @@
},
"args": {}
},
- "required": [
- "state",
- "toolCallId",
- "toolName"
- ]
+ "required": ["state", "toolCallId", "toolName"]
},
"Message.ToolInvocation.ToolResult": {
"type": "object",
@@ -995,12 +905,7 @@
"type": "string"
}
},
- "required": [
- "state",
- "toolCallId",
- "toolName",
- "result"
- ]
+ "required": ["state", "toolCallId", "toolName", "result"]
},
"Message.Part.SourceUrl": {
"type": "object",
@@ -1023,11 +928,7 @@
"additionalProperties": {}
}
},
- "required": [
- "type",
- "sourceId",
- "url"
- ]
+ "required": ["type", "sourceId", "url"]
},
"Message.Part.File": {
"type": "object",
@@ -1046,11 +947,7 @@
"type": "string"
}
},
- "required": [
- "type",
- "mediaType",
- "url"
- ]
+ "required": ["type", "mediaType", "url"]
},
"Message.Part.StepStart": {
"type": "object",
@@ -1060,9 +957,7 @@
"const": "step-start"
}
},
- "required": [
- "type"
- ]
+ "required": ["type"]
},
"Message.Metadata": {
"type": "object",
@@ -1077,9 +972,7 @@
"type": "number"
}
},
- "required": [
- "created"
- ]
+ "required": ["created"]
},
"error": {
"oneOf": [
@@ -1119,16 +1012,10 @@
"type": "number"
}
},
- "required": [
- "start",
- "end"
- ]
+ "required": ["start", "end"]
}
},
- "required": [
- "title",
- "time"
- ],
+ "required": ["title", "time"],
"additionalProperties": {}
}
},
@@ -1157,10 +1044,7 @@
"type": "string"
}
},
- "required": [
- "cwd",
- "root"
- ]
+ "required": ["cwd", "root"]
},
"cost": {
"type": "number"
@@ -1190,18 +1074,10 @@
"type": "number"
}
},
- "required": [
- "read",
- "write"
- ]
+ "required": ["read", "write"]
}
},
- "required": [
- "input",
- "output",
- "reasoning",
- "cache"
- ]
+ "required": ["input", "output", "reasoning", "cache"]
}
},
"required": [
@@ -1214,11 +1090,7 @@
]
}
},
- "required": [
- "time",
- "sessionID",
- "tool"
- ]
+ "required": ["time", "sessionID", "tool"]
},
"ProviderAuthError": {
"type": "object",
@@ -1237,16 +1109,10 @@
"type": "string"
}
},
- "required": [
- "providerID",
- "message"
- ]
+ "required": ["providerID", "message"]
}
},
- "required": [
- "name",
- "data"
- ]
+ "required": ["name", "data"]
},
"UnknownError": {
"type": "object",
@@ -1262,15 +1128,10 @@
"type": "string"
}
},
- "required": [
- "message"
- ]
+ "required": ["message"]
}
},
- "required": [
- "name",
- "data"
- ]
+ "required": ["name", "data"]
},
"Event.message.part.updated": {
"type": "object",
@@ -1292,17 +1153,10 @@
"type": "string"
}
},
- "required": [
- "part",
- "sessionID",
- "messageID"
- ]
+ "required": ["part", "sessionID", "messageID"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"Event.session.updated": {
"type": "object",
@@ -1318,15 +1172,10 @@
"$ref": "#/components/schemas/session.info"
}
},
- "required": [
- "info"
- ]
+ "required": ["info"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"session.info": {
"type": "object",
@@ -1346,9 +1195,7 @@
"type": "string"
}
},
- "required": [
- "url"
- ]
+ "required": ["url"]
},
"title": {
"type": "string"
@@ -1366,18 +1213,10 @@
"type": "number"
}
},
- "required": [
- "created",
- "updated"
- ]
+ "required": ["created", "updated"]
}
},
- "required": [
- "id",
- "title",
- "version",
- "time"
- ]
+ "required": ["id", "title", "version", "time"]
},
"Event.session.deleted": {
"type": "object",
@@ -1393,15 +1232,10 @@
"$ref": "#/components/schemas/session.info"
}
},
- "required": [
- "info"
- ]
+ "required": ["info"]
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"Event.session.error": {
"type": "object",
@@ -1433,10 +1267,7 @@
}
}
},
- "required": [
- "type",
- "properties"
- ]
+ "required": ["type", "properties"]
},
"App.Info": {
"type": "object",
@@ -1466,13 +1297,7 @@
"type": "string"
}
},
- "required": [
- "config",
- "data",
- "root",
- "cwd",
- "state"
- ]
+ "required": ["config", "data", "root", "cwd", "state"]
},
"time": {
"type": "object",
@@ -1483,12 +1308,7 @@
}
}
},
- "required": [
- "user",
- "git",
- "path",
- "time"
- ]
+ "required": ["user", "git", "path", "time"]
},
"Config.Info": {
"type": "object",
@@ -1583,10 +1403,7 @@
"type": "number"
}
},
- "required": [
- "input",
- "output"
- ]
+ "required": ["input", "output"]
},
"limit": {
"type": "object",
@@ -1598,10 +1415,7 @@
"type": "number"
}
},
- "required": [
- "context",
- "output"
- ]
+ "required": ["context", "output"]
},
"id": {
"type": "string"
@@ -1618,9 +1432,7 @@
"additionalProperties": {}
}
},
- "required": [
- "models"
- ]
+ "required": ["models"]
},
"description": "Custom provider configurations and model overrides"
},
@@ -1790,12 +1602,7 @@
}
}
},
- "required": [
- "name",
- "env",
- "id",
- "models"
- ]
+ "required": ["name", "env", "id", "models"]
},
"Model.Info": {
"type": "object",
@@ -1831,10 +1638,7 @@
"type": "number"
}
},
- "required": [
- "input",
- "output"
- ]
+ "required": ["input", "output"]
},
"limit": {
"type": "object",
@@ -1846,10 +1650,7 @@
"type": "number"
}
},
- "required": [
- "context",
- "output"
- ]
+ "required": ["context", "output"]
},
"id": {
"type": "string"
@@ -1894,10 +1695,7 @@
"description": "Environment variables to set when running the MCP server"
}
},
- "required": [
- "type",
- "command"
- ],
+ "required": ["type", "command"],
"additionalProperties": false
},
"Config.McpRemote": {
@@ -1913,10 +1711,7 @@
"description": "URL of the remote MCP server"
}
},
- "required": [
- "type",
- "url"
- ],
+ "required": ["type", "url"],
"additionalProperties": false
},
"Error": {
@@ -1927,9 +1722,7 @@
"additionalProperties": {}
}
},
- "required": [
- "data"
- ]
+ "required": ["data"]
},
"InstallationInfo": {
"type": "object",
@@ -1941,11 +1734,8 @@
"type": "string"
}
},
- "required": [
- "version",
- "latest"
- ]
+ "required": ["version", "latest"]
}
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/web/astro.config.mjs b/packages/web/astro.config.mjs
index e1b3496d0..9099c6df3 100644
--- a/packages/web/astro.config.mjs
+++ b/packages/web/astro.config.mjs
@@ -32,9 +32,7 @@ export default defineConfig({
starlight({
title: "opencode",
expressiveCode: { themes: ["github-light", "github-dark"] },
- social: [
- { icon: "github", label: "GitHub", href: config.github },
- ],
+ social: [{ icon: "github", label: "GitHub", href: config.github }],
head: [
{
tag: "link",
--
cgit v1.2.3
From 49053b66a92e7d1095d7934ff86a6764ed765266 Mon Sep 17 00:00:00 2001
From: adamdottv <2363879+adamdottv@users.noreply.github.com>
Date: Fri, 27 Jun 2025 06:48:34 -0500
Subject: fix(web): remove system prompts from share page
---
packages/web/src/components/Share.tsx | 91 ++++-------------------------------
1 file changed, 10 insertions(+), 81 deletions(-)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index 5510f5a69..9b953bd2d 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -23,7 +23,6 @@ import {
} from "./icons/custom"
import {
IconFolder,
- IconCpuChip,
IconHashtag,
IconSparkles,
IconGlobeAlt,
@@ -103,10 +102,7 @@ function stripWorkingDirectory(filePath: string, workingDir?: string) {
}
function getShikiLang(filename: string) {
- const ext = filename
- .split('.')
- .pop()
- ?.toLowerCase() ?? ''
+ const ext = filename.split(".").pop()?.toLowerCase() ?? ""
// map.languages(ext) returns an array of matching Linguist language names (e.g. ['TypeScript'])
const langs = map.languages(ext)
@@ -114,12 +110,10 @@ function getShikiLang(filename: string) {
// Overrride any specific language mappings
const overrides: Record = {
- "conf": "shellscript"
+ conf: "shellscript",
}
- return type
- ? overrides[type] ?? type
- : 'plaintext'
+ return type ? (overrides[type] ?? type) : "plaintext"
}
function formatDuration(ms: number): string {
@@ -1087,13 +1081,6 @@ export default function Share(props: {
}
>
{(assistant) => {
- const system = createMemo(() => {
- const prompts = assistant().system || []
- return prompts.filter(
- (p: string) =>
- !p.startsWith("You are Claude"),
- )
- })
return (
{assistant().modelID}
-
0}>
-
-
- setShowResults((e) => !e)
- }
- />
-
-
-
-
-
)
}}
- {/* System text */}
-
- {(part) => (
-
- )}
-
+
{/* Grep tool */}
toolData()?.args.path !== data().rootDir
? stripWorkingDirectory(
- toolData()?.args.path,
- data().rootDir,
- )
+ toolData()?.args.path,
+ data().rootDir,
+ )
: toolData()?.args.path,
)
@@ -1737,8 +1670,7 @@ export default function Share(props: {
when={
msg.role === "assistant" &&
part.type === "tool-invocation" &&
- part.toolInvocation.toolName ===
- "todowrite" &&
+ part.toolInvocation.toolName === "todowrite" &&
part
}
>
@@ -1803,8 +1735,7 @@ export default function Share(props: {
when={
msg.role === "assistant" &&
part.type === "tool-invocation" &&
- part.toolInvocation.toolName ===
- "webfetch" &&
+ part.toolInvocation.toolName === "webfetch" &&
part
}
>
@@ -1978,9 +1909,7 @@ export default function Share(props: {
>
-
-
-
+
--
cgit v1.2.3
From 87d62514dbce9fc0222a99d092c86ea7e6314cbb Mon Sep 17 00:00:00 2001
From: Jay V
Date: Fri, 27 Jun 2025 13:25:15 -0400
Subject: docs: share page write tool bug
---
packages/web/src/components/Share.tsx | 14 +++++++-------
packages/web/src/types/lang-map.d.ts | 27 +++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 7 deletions(-)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index 9b953bd2d..e8879c50a 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -85,7 +85,7 @@ function scrollToAnchor(id: string) {
el.scrollIntoView({ behavior: "smooth" })
}
-function stripWorkingDirectory(filePath: string, workingDir?: string) {
+function stripWorkingDirectory(filePath?: string, workingDir?: string) {
if (filePath === undefined || workingDir === undefined) return filePath
const prefix = workingDir.endsWith("/") ? workingDir : workingDir + "/"
@@ -1307,9 +1307,9 @@ export default function Share(props: {
const path = createMemo(() =>
toolData()?.args.path !== data().rootDir
? stripWorkingDirectory(
- toolData()?.args.path,
- data().rootDir,
- )
+ toolData()?.args.path,
+ data().rootDir,
+ )
: toolData()?.args.path,
)
@@ -1470,7 +1470,7 @@ export default function Share(props: {
{(_part) => {
const filePath = createMemo(() =>
stripWorkingDirectory(
- toolData()?.args.filePath,
+ toolData()?.args?.filePath,
data().rootDir,
),
)
@@ -1509,7 +1509,7 @@ export default function Share(props: {
{formatErrorString(
- toolData()?.result,
+ toolData()?.result
)}
@@ -1528,7 +1528,7 @@ export default function Share(props: {
diff --git a/packages/web/src/types/lang-map.d.ts b/packages/web/src/types/lang-map.d.ts
index e69de29bb..b21d2a005 100644
--- a/packages/web/src/types/lang-map.d.ts
+++ b/packages/web/src/types/lang-map.d.ts
@@ -0,0 +1,27 @@
+declare module "lang-map" {
+ /** Returned by calling `map()` */
+ export interface MapReturn {
+ /** All extensions keyed by language name */
+ extensions: Record;
+ /** All languages keyed by file-extension */
+ languages: Record;
+ }
+
+ /**
+ * Calling `map()` gives you the raw lookup tables:
+ *
+ * ```js
+ * const { extensions, languages } = map();
+ * ```
+ */
+ function map(): MapReturn;
+
+ /** Static method: get extensions for a given language */
+ namespace map {
+ function extensions(language: string): string[];
+ /** Static method: get languages for a given extension */
+ function languages(extension: string): string[];
+ }
+
+ export = map;
+}
--
cgit v1.2.3
From fa2723f2d0033c5b566abea035062e799a8634c6 Mon Sep 17 00:00:00 2001
From: Jay V
Date: Fri, 27 Jun 2025 14:04:09 -0400
Subject: docs: update logo screenshot
---
README.md | 8 ++++----
packages/web/public/social-share.png | Bin 17103 -> 17520 bytes
.../web/src/assets/lander/screenshot-splash.png | Bin 0 -> 467281 bytes
packages/web/src/assets/lander/screenshot.png | Bin 0 -> 552769 bytes
packages/web/src/assets/logo-ornate-dark.svg | 18 ++++++++++++++++++
packages/web/src/assets/logo-ornate-light.svg | 18 ++++++++++++++++++
packages/web/src/assets/themes/ayu.png | Bin 449635 -> 0 bytes
packages/web/src/assets/themes/everforest.png | Bin 448402 -> 0 bytes
packages/web/src/assets/themes/opencode.png | Bin 442445 -> 0 bytes
packages/web/src/assets/themes/tokyonight.png | Bin 452738 -> 0 bytes
packages/web/src/components/Lander.astro | 2 +-
packages/web/src/content/docs/docs/index.mdx | 2 +-
packages/web/src/content/docs/index.mdx | 4 ++--
13 files changed, 44 insertions(+), 8 deletions(-)
create mode 100644 packages/web/src/assets/lander/screenshot-splash.png
create mode 100644 packages/web/src/assets/lander/screenshot.png
create mode 100644 packages/web/src/assets/logo-ornate-dark.svg
create mode 100644 packages/web/src/assets/logo-ornate-light.svg
delete mode 100644 packages/web/src/assets/themes/ayu.png
delete mode 100644 packages/web/src/assets/themes/everforest.png
delete mode 100644 packages/web/src/assets/themes/opencode.png
delete mode 100644 packages/web/src/assets/themes/tokyonight.png
(limited to 'packages/web')
diff --git a/README.md b/README.md
index 6cbe9619a..9aa47f988 100644
--- a/README.md
+++ b/README.md
@@ -1,9 +1,9 @@
-
-
-
+
+
+
@@ -14,7 +14,7 @@
-[](https://opencode.ai)
+[](https://opencode.ai)
---
diff --git a/packages/web/public/social-share.png b/packages/web/public/social-share.png
index 39b86514c..97f67994d 100644
Binary files a/packages/web/public/social-share.png and b/packages/web/public/social-share.png differ
diff --git a/packages/web/src/assets/lander/screenshot-splash.png b/packages/web/src/assets/lander/screenshot-splash.png
new file mode 100644
index 000000000..e900673ef
Binary files /dev/null and b/packages/web/src/assets/lander/screenshot-splash.png differ
diff --git a/packages/web/src/assets/lander/screenshot.png b/packages/web/src/assets/lander/screenshot.png
new file mode 100644
index 000000000..d49a62b4d
Binary files /dev/null and b/packages/web/src/assets/lander/screenshot.png differ
diff --git a/packages/web/src/assets/logo-ornate-dark.svg b/packages/web/src/assets/logo-ornate-dark.svg
new file mode 100644
index 000000000..b937be0af
--- /dev/null
+++ b/packages/web/src/assets/logo-ornate-dark.svg
@@ -0,0 +1,18 @@
+
diff --git a/packages/web/src/assets/logo-ornate-light.svg b/packages/web/src/assets/logo-ornate-light.svg
new file mode 100644
index 000000000..b519ec41a
--- /dev/null
+++ b/packages/web/src/assets/logo-ornate-light.svg
@@ -0,0 +1,18 @@
+
diff --git a/packages/web/src/assets/themes/ayu.png b/packages/web/src/assets/themes/ayu.png
deleted file mode 100644
index 0c4af5026..000000000
Binary files a/packages/web/src/assets/themes/ayu.png and /dev/null differ
diff --git a/packages/web/src/assets/themes/everforest.png b/packages/web/src/assets/themes/everforest.png
deleted file mode 100644
index 611e95530..000000000
Binary files a/packages/web/src/assets/themes/everforest.png and /dev/null differ
diff --git a/packages/web/src/assets/themes/opencode.png b/packages/web/src/assets/themes/opencode.png
deleted file mode 100644
index cc5c5d007..000000000
Binary files a/packages/web/src/assets/themes/opencode.png and /dev/null differ
diff --git a/packages/web/src/assets/themes/tokyonight.png b/packages/web/src/assets/themes/tokyonight.png
deleted file mode 100644
index 1d39ba075..000000000
Binary files a/packages/web/src/assets/themes/tokyonight.png and /dev/null differ
diff --git a/packages/web/src/components/Lander.astro b/packages/web/src/components/Lander.astro
index 82c33f3b3..596bca2d7 100644
--- a/packages/web/src/components/Lander.astro
+++ b/packages/web/src/components/Lander.astro
@@ -5,7 +5,7 @@ import type { Props } from '@astrojs/starlight/props';
import CopyIcon from "../assets/lander/copy.svg";
import CheckIcon from "../assets/lander/check.svg";
-import Screenshot from "../assets/themes/tokyonight.png";
+import Screenshot from "../assets/lander/screenshot-splash.png";
const { data } = Astro.locals.starlightRoute.entry;
const { title = data.title, tagline, image, actions = [] } = data.hero || {};
diff --git a/packages/web/src/content/docs/docs/index.mdx b/packages/web/src/content/docs/docs/index.mdx
index 0a8a8fa25..4926450c7 100644
--- a/packages/web/src/content/docs/docs/index.mdx
+++ b/packages/web/src/content/docs/docs/index.mdx
@@ -13,7 +13,7 @@ import { Tabs, TabItem } from '@astrojs/starlight/components';
- Log in with Anthropic to use your Claude Pro or Claude Max account.
- Supports 75+ LLM providers through [Models.dev](https://models.dev), including local models.
-
+
---
diff --git a/packages/web/src/content/docs/index.mdx b/packages/web/src/content/docs/index.mdx
index 1f735db21..ea39be9ee 100644
--- a/packages/web/src/content/docs/index.mdx
+++ b/packages/web/src/content/docs/index.mdx
@@ -6,7 +6,7 @@ hero:
title: The AI coding agent built for the terminal.
tagline: The AI coding agent built for the terminal.
image:
- dark: ../../assets/logo-dark.svg
- light: ../../assets/logo-light.svg
+ dark: ../../assets/logo-ornate-dark.svg
+ light: ../../assets/logo-ornate-light.svg
alt: opencode logo
---
--
cgit v1.2.3
From 289797f56dbe7a7b51bc74ef8413da1a41a1b95b Mon Sep 17 00:00:00 2001
From: Jay V
Date: Fri, 27 Jun 2025 15:31:10 -0400
Subject: docs: share cleanup title
---
packages/web/src/components/Share.tsx | 122 +++++++++++++--------------
packages/web/src/components/share.module.css | 34 ++++++--
2 files changed, 89 insertions(+), 67 deletions(-)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index e8879c50a..b48d04662 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -859,59 +859,15 @@ export default function Share(props: {
{store.info?.title}
-
- -
- Cost
- {data().cost !== undefined ? (
- ${data().cost.toFixed(2)}
- ) : (
- —
- )}
-
- -
- Input Tokens
- {data().tokens.input ? (
- {data().tokens.input}
- ) : (
- —
- )}
-
- -
- Output Tokens
- {data().tokens.output ? (
- {data().tokens.output}
- ) : (
- —
- )}
-
- -
- Reasoning Tokens
- {data().tokens.reasoning ? (
- {data().tokens.reasoning}
- ) : (
- —
- )}
-
-
-
-
- -
-
-
-
- {data().rootDir}
-
- -
-
-
-
-
- v{store.info?.version}
-
-
-
-
+ -
+
+
+
+
+ v{store.info?.version}
+
+
{Object.values(data().models).length > 0 ? (
{([provider, model]) => (
@@ -1305,12 +1261,12 @@ export default function Share(props: {
>
{(_part) => {
const path = createMemo(() =>
- toolData()?.args.path !== data().rootDir
+ toolData()?.args?.path !== data().rootDir
? stripWorkingDirectory(
- toolData()?.args.path,
+ toolData()?.args?.path,
data().rootDir,
)
- : toolData()?.args.path,
+ : toolData()?.args?.path,
)
return (
@@ -1332,7 +1288,9 @@ export default function Share(props: {
LS
- {path()}
+
+ {path()}
+
@@ -1375,7 +1333,7 @@ export default function Share(props: {
{(_part) => {
const filePath = createMemo(() =>
stripWorkingDirectory(
- toolData()?.args.filePath,
+ toolData()?.args?.filePath,
data().rootDir,
),
)
@@ -1398,7 +1356,9 @@ export default function Share(props: {
Read
- {filePath()}
+
+ {filePath()}
+
@@ -1499,7 +1459,9 @@ export default function Share(props: {
Write
- {filePath()}
+
+ {filePath()}
+
0}>
{diagnostics()}
@@ -1586,7 +1548,9 @@ export default function Share(props: {
Edit
- {filePath()}
+
+ {filePath()}
+
@@ -1935,13 +1899,47 @@ export default function Share(props: {
)}
-
+
-
{getStatusText(connectionStatus())}
+
{getStatusText(connectionStatus())}
+
+ -
+ Cost
+ {data().cost !== undefined ? (
+ ${data().cost.toFixed(2)}
+ ) : (
+ —
+ )}
+
+ -
+ Input Tokens
+ {data().tokens.input ? (
+ {data().tokens.input}
+ ) : (
+ —
+ )}
+
+ -
+ Output Tokens
+ {data().tokens.output ? (
+ {data().tokens.output}
+ ) : (
+ —
+ )}
+
+ -
+ Reasoning Tokens
+ {data().tokens.reasoning ? (
+ {data().tokens.reasoning}
+ ) : (
+ —
+ )}
+
+
diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css
index a52fd1765..41b257dad 100644
--- a/packages/web/src/components/share.module.css
+++ b/packages/web/src/components/share.module.css
@@ -89,7 +89,7 @@
padding: 0;
margin: 0;
display: flex;
- gap: 0.5rem 1rem;
+ gap: 0.5rem 0.875rem;
flex-wrap: wrap;
li {
@@ -104,8 +104,7 @@
}
}
- [data-section="stats"][data-section-root],
- [data-section="stats"][data-section-models] {
+ [data-section="stats"] {
li {
gap: 0.3125rem;
@@ -375,7 +374,7 @@
}
}
}
- [data-part-type="connection-status"] {
+ [data-part-type="summary"] {
& > [data-section="decoration"] {
span:first-child {
flex: 0 0 auto;
@@ -405,12 +404,37 @@
}
& > [data-section="content"] {
- span {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+
+ p[data-section="copy"] {
display: block;
line-height: 18px;
font-size: 0.875rem;
color: var(--sl-color-text-dimmed);
}
+
+ [data-section="stats"] {
+ list-style-type: none;
+ padding: 0;
+ margin: 0;
+ display: flex;
+ gap: 0.5rem 0.875rem;
+ flex-wrap: wrap;
+
+ li {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ font-size: 0.75rem;
+ color: var(--sl-color-text-secondary);
+
+ span[data-placeholder] {
+ color: var(--sl-color-text-dimmed);
+ }
+ }
+ }
}
}
}
--
cgit v1.2.3
From 145df084440470bb53655f62b5f5588e2615f1ba Mon Sep 17 00:00:00 2001
From: Jay V
Date: Fri, 27 Jun 2025 19:16:33 -0400
Subject: docs: share page format
---
packages/web/src/components/Share.tsx | 67 ++++++++++++----------------
packages/web/src/components/share.module.css | 9 +---
2 files changed, 30 insertions(+), 46 deletions(-)
(limited to 'packages/web')
diff --git a/packages/web/src/components/Share.tsx b/packages/web/src/components/Share.tsx
index b48d04662..008f863c9 100644
--- a/packages/web/src/components/Share.tsx
+++ b/packages/web/src/components/Share.tsx
@@ -595,12 +595,17 @@ export default function Share(props: {
info: Session.Info
messages: Record
}) {
+ let lastScrollY = 0
let hasScrolled = false
+ let scrollTimeout: number | undefined
const id = props.id
const params = new URLSearchParams(window.location.search)
const debug = params.get("debug") === "true"
+ const [showScrollButton, setShowScrollButton] = createSignal(false)
+ const [isButtonHovered, setIsButtonHovered] = createSignal(false)
+
const anchorId = createMemo(() => {
const raw = window.location.hash.slice(1)
const [id] = raw.split("-")
@@ -716,23 +721,18 @@ export default function Share(props: {
})
})
- const [showScrollButton, setShowScrollButton] = createSignal(false)
- const [isButtonHovered, setIsButtonHovered] = createSignal(false)
- let scrollTimeout: number | undefined
- let lastScrollY = 0
-
- const checkScrollNeed = () => {
+ function checkScrollNeed() {
const currentScrollY = window.scrollY
const isScrollingDown = currentScrollY > lastScrollY
const scrolled = currentScrollY > 200 // Show after scrolling 200px
const isNearBottom = window.innerHeight + currentScrollY >= document.body.scrollHeight - 100
-
+
// Only show when scrolling down, scrolled enough, and not near bottom
const shouldShow = isScrollingDown && scrolled && !isNearBottom
-
+
// Update last scroll position
lastScrollY = currentScrollY
-
+
if (shouldShow) {
setShowScrollButton(true)
// Clear existing timeout
@@ -754,30 +754,6 @@ export default function Share(props: {
}
}
- const handleButtonMouseEnter = () => {
- setIsButtonHovered(true)
- // Clear timeout when hovering
- if (scrollTimeout) {
- clearTimeout(scrollTimeout)
- }
- }
-
- const handleButtonMouseLeave = () => {
- setIsButtonHovered(false)
- // Restart timeout when leaving hover
- if (showScrollButton()) {
- scrollTimeout = window.setTimeout(() => {
- if (!isButtonHovered()) {
- setShowScrollButton(false)
- }
- }, 3000)
- }
- }
-
- const scrollToBottom = () => {
- document.body.scrollIntoView({ behavior: "smooth", block: "end" })
- }
-
onMount(() => {
lastScrollY = window.scrollY // Initialize scroll position
checkScrollNeed()
@@ -1982,14 +1958,29 @@ export default function Share(props: {
- {/* Floating scroll to bottom button */}
+ document.body.scrollIntoView({ behavior: "smooth", block: "end" })
+ }
+ onMouseEnter={() => {
+ setIsButtonHovered(true)
+ if (scrollTimeout) {
+ clearTimeout(scrollTimeout)
+ }
+ }}
+ onMouseLeave={() => {
+ setIsButtonHovered(false)
+ if (showScrollButton()) {
+ scrollTimeout = window.setTimeout(() => {
+ if (!isButtonHovered()) {
+ setShowScrollButton(false)
+ }
+ }, 3000)
+ }
+ }}
title="Scroll to bottom"
aria-label="Scroll to bottom"
>
diff --git a/packages/web/src/components/share.module.css b/packages/web/src/components/share.module.css
index 41b257dad..dafbdd8a2 100644
--- a/packages/web/src/components/share.module.css
+++ b/packages/web/src/components/share.module.css
@@ -785,7 +785,7 @@
}
}
-.scrollButton {
+.scroll-button {
position: fixed;
bottom: 2rem;
right: 2rem;
@@ -799,18 +799,11 @@
display: flex;
align-items: center;
justify-content: center;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.15s ease, opacity 0.5s ease;
z-index: 100;
appearance: none;
opacity: 1;
- &:hover {
- color: var(--sl-color-text);
- border-color: var(--sl-color-hairline);
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
- }
-
&:active {
transform: translateY(1px);
}
--
cgit v1.2.3