Skip to content

Commit bce2d3c

Browse files
sheremet-vahi-ogawaOpenCode
authored
feat(ui): highlight editor source locations (#11004)
Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode <noreply@opencode.ai>
1 parent 1f4fcd3 commit bce2d3c

6 files changed

Lines changed: 33 additions & 7 deletions

File tree

‎packages/ui/client/components/CodeMirrorContainer.vue‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,16 @@ onMounted(async () => {
7171
</script>
7272

7373
<template>
74-
<div relative font-mono text-sm class="codemirror-scrolls" :class="saving ? 'codemirror-busy' : undefined">
74+
<div
75+
relative
76+
font-mono
77+
text-sm
78+
class="codemirror-scrolls"
79+
:class="{
80+
'codemirror-busy': saving,
81+
'codemirror-hide-cursor': readOnly,
82+
}"
83+
>
7584
<textarea ref="el" />
7685
</div>
7786
</template>

‎packages/ui/client/components/views/ViewEditor.vue‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,6 +459,7 @@ onBeforeUnmount(clearListeners)
459459
lineNumbers: true,
460460
readOnly: isReport || !config.api?.allowWrite,
461461
saving,
462+
styleActiveLine: true,
462463
gutters: ['CodeMirror-linenumbers', ...traceGutterConfigs],
463464
}"
464465
:mode="ext"

‎packages/ui/client/composables/codemirror.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'codemirror/mode/xml/xml'
1313
import 'codemirror/mode/htmlmixed/htmlmixed'
1414
import 'codemirror/mode/jsx/jsx'
1515
import 'codemirror/addon/display/placeholder'
16+
import 'codemirror/addon/selection/active-line'
1617
import 'codemirror/addon/scroll/simplescrollbars'
1718
import 'codemirror/addon/scroll/simplescrollbars.css'
1819

‎packages/ui/client/styles/main.css‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ html.dark {
5151
--cm-regex: #ab5e3f;
5252
--cm-json-property: #698c96;
5353
--cm-line-number-gutter: #f8f8f8;
54+
/* blue-500, with higher opacity in dark mode */
55+
--cm-line-highlight-background: rgb(59 130 246 / 10%);
5456
/* scrollbars colors */
5557
--cm-ttc-c-thumb: #eee;
5658
--cm-ttc-c-track: white;
@@ -79,13 +81,25 @@ html.dark {
7981
--cm-json-property: #6b8b9e;
8082
--cm-line-number: #888888;
8183
--cm-line-number-gutter: #161616;
82-
--cm-line-highlight-background: #444444;
84+
--cm-line-highlight-background: rgb(59 130 246 / 18%);
8385
--cm-selection-background: #44444450;
8486
/* scrollbars colors */
8587
--cm-ttc-c-thumb: #222;
8688
--cm-ttc-c-track: #111;
8789
}
8890

91+
.CodeMirror-activeline-background {
92+
background: var(--cm-line-highlight-background);
93+
}
94+
95+
/*
96+
* code mirror has readOnly: 'nocursor' option, but it would disable editor input and prevent focus.
97+
* this only hides the cursor caret while keeping the editor region focusable and selectable.
98+
*/
99+
.codemirror-hide-cursor .CodeMirror-cursors {
100+
visibility: hidden !important;
101+
}
102+
89103
.splitpanes__pane {
90104
background-color: unset !important;
91105
}

‎test/ui/test/trace.spec.ts‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import type { Page } from '@playwright/test'
22
import type { PreviewServer } from 'vite'
33
import type { Vitest } from 'vitest/node'
44
import { expect, test } from '@playwright/test'
5-
import { assertTestCounts, evaluateEditor, openExplorerItem, startHtmlReportPreview, startVitestUi } from './helper'
5+
import { assertTestCounts, openExplorerItem, startHtmlReportPreview, startVitestUi } from './helper'
66

77
test.describe('ui', () => {
88
let vitest: Vitest | undefined
@@ -152,9 +152,9 @@ async function testBasic(page: Page) {
152152
await traceStepNames.getByText('Render simple').click()
153153
await expect(page.getByTestId('btn-code')).toContainClass('tab-button-active')
154154

155-
// verify editor cursor position
156-
const getEditorCursor = () => evaluateEditor(page, editor => editor.getCursor())
157-
await expect.poll(() => getEditorCursor()).toEqual({ line: 9, ch: 33 })
155+
// verify source location highlight
156+
const activeLine = page.getByTestId('editor').locator('.CodeMirror-activeline')
157+
await expect(activeLine).toContainText('Render simple')
158158

159159
// markers ordered by 'test finished' > 'Render simple' > 'Render another'
160160
const traceEditorMarkers = page.getByTestId('editor').getByTestId('trace-editor-marker')
@@ -172,7 +172,7 @@ async function testBasic(page: Page) {
172172
// selecting 2nd trace step and verify again
173173
await traceStepNames.getByText('Render another').click()
174174
await expect(traceFrame.getByRole('button', { name: 'Another' })).toBeVisible()
175-
await expect.poll(() => getEditorCursor()).toEqual({ line: 12, ch: 33 })
175+
await expect(activeLine).toContainText('Render another')
176176
await expect(traceSteps.nth(1)).toHaveAttribute('aria-current', 'step')
177177
await expect(traceEditorMarkers.nth(1)).not.toHaveAttribute('aria-current', 'step')
178178
await expect(traceEditorMarkers.nth(2)).toHaveAttribute('aria-current', 'step')

‎test/ui/test/ui.spec.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,7 @@ async function testWriteFile(page: Page, options: { enabled: boolean }) {
661661
await codeTabButton.click()
662662
const editor = page.getByTestId('editor')
663663
await expect(editor).toContainText('expect(1 + 1).toEqual(2)')
664+
await expect(editor.locator('.CodeMirror-cursors')).toHaveCSS('visibility', options.enabled ? 'visible' : 'hidden')
664665
await page.keyboard.type('\n// edited \n')
665666
if (options.enabled) {
666667
await expect(editor).toContainText('// edited')

0 commit comments

Comments
 (0)