mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-09-13 18:13:37 +00:00
added a line number column that syncs to content and scroll
This commit is contained in:
parent
eed11fd9c6
commit
6c3906b04c
@ -1 +1,4 @@
|
|||||||
<div @ref="this.editorElement" class="code-editor @this.Class" style="@this.CodeEditorThemeStyle"></div>
|
<div class="code-editor @this.Class" style="@this.CodeEditorThemeStyle">
|
||||||
|
<div @ref="this.lineNumbersElement" class="code-editor-line-numbers" aria-hidden="true"></div>
|
||||||
|
<div @ref="this.editorElement" class="code-editor-input"></div>
|
||||||
|
</div>
|
||||||
|
|||||||
@ -23,8 +23,9 @@ public partial class CodeEditor : ComponentBase, IAsyncDisposable
|
|||||||
public string Class { get; set; } = string.Empty;
|
public string Class { get; set; } = string.Empty;
|
||||||
|
|
||||||
private readonly string editorId = $"code-editor-{Guid.NewGuid():N}";
|
private readonly string editorId = $"code-editor-{Guid.NewGuid():N}";
|
||||||
private const string CODE_EDITOR_MODULE = "./system/CodeEditor/code-editor.js?v=20260707-3";
|
private const string CODE_EDITOR_MODULE = "./system/CodeEditor/code-editor.js?v=20260708-1";
|
||||||
private ElementReference editorElement;
|
private ElementReference editorElement;
|
||||||
|
private ElementReference lineNumbersElement;
|
||||||
private IJSObjectReference? module;
|
private IJSObjectReference? module;
|
||||||
private string CodeEditorThemeStyle => this.GetCodeEditorThemeStyle();
|
private string CodeEditorThemeStyle => this.GetCodeEditorThemeStyle();
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ public partial class CodeEditor : ComponentBase, IAsyncDisposable
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
this.module = await this.JsRuntime.InvokeAsync<IJSObjectReference>("import", CODE_EDITOR_MODULE);
|
this.module = await this.JsRuntime.InvokeAsync<IJSObjectReference>("import", CODE_EDITOR_MODULE);
|
||||||
await this.module.InvokeVoidAsync("init", this.editorId, this.editorElement, this.Value, this.Language.ToString());
|
await this.module.InvokeVoidAsync("init", this.editorId, this.editorElement, this.lineNumbersElement, this.Value, this.Language.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<string> GetCodeAsync()
|
public async ValueTask<string> GetCodeAsync()
|
||||||
|
|||||||
@ -204,21 +204,45 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.code-editor {
|
.code-editor {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(2.8rem, auto) minmax(0, 1fr);
|
||||||
min-height: 32rem;
|
min-height: 32rem;
|
||||||
height: 62vh;
|
height: 62vh;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: auto;
|
overflow: hidden;
|
||||||
padding: 0.8rem;
|
|
||||||
border: 1px solid var(--mw-code-editor-border, rgba(0,0,0,0.11764705882352941));
|
border: 1px solid var(--mw-code-editor-border, rgba(0,0,0,0.11764705882352941));
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: var(--mw-code-editor-background, rgba(255,255,255,1));
|
background: var(--mw-code-editor-background, rgba(255,255,255,1));
|
||||||
color: var(--mw-code-editor-foreground, rgba(66,66,66,1));
|
color: var(--mw-code-editor-foreground, rgba(66,66,66,1));
|
||||||
caret-color: var(--mw-code-editor-foreground, rgba(66,66,66,1));
|
|
||||||
font-family: "JetBrains Mono", monospace;
|
font-family: "JetBrains Mono", monospace;
|
||||||
font-size: 0.65rem;
|
font-size: 0.65rem;
|
||||||
line-height: 1.45;
|
line-height: 1.45;
|
||||||
white-space: pre;
|
|
||||||
tab-size: 4;
|
tab-size: 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor-line-numbers {
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 0.8rem 0.65rem 0.8rem 0.5rem;
|
||||||
|
border-right: 1px solid var(--mw-code-editor-border, rgba(0,0,0,0.11764705882352941));
|
||||||
|
color: var(--mw-code-editor-foreground, rgba(66,66,66,1));
|
||||||
|
opacity: 0.55;
|
||||||
|
text-align: right;
|
||||||
|
white-space: pre;
|
||||||
|
user-select: none;
|
||||||
|
font: inherit;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-editor-input {
|
||||||
|
min-width: 0;
|
||||||
|
height: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
padding: 0.8rem;
|
||||||
|
color: var(--mw-code-editor-foreground, rgba(66,66,66,1));
|
||||||
|
caret-color: var(--mw-code-editor-foreground, rgba(66,66,66,1));
|
||||||
|
font: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
tab-size: inherit;
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -27,21 +27,25 @@ const LUA_BUILT_INS = new Set([
|
|||||||
* The highlighter callback receives the editor DOM node. It must write highlighted
|
* The highlighter callback receives the editor DOM node. It must write highlighted
|
||||||
* HTML back into that node, so every token emitted by our highlighter is HTML-escaped.
|
* HTML back into that node, so every token emitted by our highlighter is HTML-escaped.
|
||||||
*/
|
*/
|
||||||
export function init(id, element, code, language) {
|
export function init(id, element, lineNumbersElement, code, language) {
|
||||||
const codeJar = CodeJar(element, getHighlighter(language), {
|
const codeJar = CodeJar(element, getHighlighter(language), {
|
||||||
tab: ' ',
|
tab: ' ',
|
||||||
spellcheck: false
|
spellcheck: false
|
||||||
});
|
});
|
||||||
|
const scrollHandler = () => syncLineNumbersScroll(element, lineNumbersElement);
|
||||||
|
|
||||||
codeJar.updateCode(code ?? '');
|
codeJar.updateCode(code ?? '');
|
||||||
editors.set(id, codeJar);
|
updateLineNumbers(lineNumbersElement, codeJar.toString());
|
||||||
|
codeJar.onUpdate(updatedCode => updateLineNumbers(lineNumbersElement, updatedCode));
|
||||||
|
element.addEventListener('scroll', scrollHandler);
|
||||||
|
editors.set(id, { codeJar, element, scrollHandler });
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current plain text from a CodeJar instance.
|
* Returns the current plain text from a CodeJar instance.
|
||||||
*/
|
*/
|
||||||
export function getCode(id) {
|
export function getCode(id) {
|
||||||
return editors.get(id)?.toString() ?? '';
|
return editors.get(id)?.codeJar.toString() ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,17 +53,43 @@ export function getCode(id) {
|
|||||||
* state stays consistent with CodeJar's internal model.
|
* state stays consistent with CodeJar's internal model.
|
||||||
*/
|
*/
|
||||||
export function setCode(id, code) {
|
export function setCode(id, code) {
|
||||||
editors.get(id)?.updateCode(code ?? '');
|
const editor = editors.get(id);
|
||||||
|
if (!editor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
editor.codeJar.updateCode(code ?? '');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes one editor instance and removes it from the JS-side registry.
|
* Disposes one editor instance and removes it from the JS-side registry.
|
||||||
*/
|
*/
|
||||||
export function destroy(id) {
|
export function destroy(id) {
|
||||||
editors.get(id)?.destroy();
|
const editor = editors.get(id);
|
||||||
|
if (!editor)
|
||||||
|
return;
|
||||||
|
|
||||||
|
editor.element.removeEventListener('scroll', editor.scrollHandler);
|
||||||
|
editor.codeJar.destroy();
|
||||||
editors.delete(id);
|
editors.delete(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateLineNumbers(lineNumbersElement, code) {
|
||||||
|
const lineCount = (code.match(/\n/g)?.length ?? 0) + 1;
|
||||||
|
let lineNumbers = '';
|
||||||
|
for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) {
|
||||||
|
if (lineNumber > 1)
|
||||||
|
lineNumbers += '\n';
|
||||||
|
|
||||||
|
lineNumbers += lineNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
lineNumbersElement.textContent = lineNumbers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncLineNumbersScroll(editorElement, lineNumbersElement) {
|
||||||
|
lineNumbersElement.scrollTop = editorElement.scrollTop;
|
||||||
|
}
|
||||||
|
|
||||||
function highlightLua(editor) {
|
function highlightLua(editor) {
|
||||||
editor.innerHTML = highlightLuaCode(editor.textContent ?? '');
|
editor.innerHTML = highlightLuaCode(editor.textContent ?? '');
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user