Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .changeset/silly-bats-own.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@modelscope-studio/pro': minor
'@modelscope-studio/frontend': minor
'modelscope_studio': minor
---

feat: add pro component `MonacoEditor`
4 changes: 2 additions & 2 deletions backend/modelscope_studio/components/base/text/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@ def __init__(
elem_style: dict | None = None,
render: bool = True,
**kwargs):
super().__init__(visible=visible,
super().__init__(value=value,
visible=visible,
elem_id=elem_id,
elem_classes=elem_classes,
render=render,
as_item=as_item,
elem_style=elem_style,
**kwargs)
self.value = value

FRONTEND_DIR = resolve_frontend_dir("text", type="base")

Expand Down
3 changes: 3 additions & 0 deletions backend/modelscope_studio/components/pro/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from .chatbot import ModelScopeProChatbot as Chatbot
from .monaco_editor import ModelScopeProMonacoEditor as MonacoEditor
from .monaco_editor.diff_editor import \
ModelScopeProMonacoEditorDiffEditor as MonacoEditorDiffEditor
from .multimodal_input import ModelScopeProMultimodalInput as MultimodalInput
from .web_sandbox import ModelScopeProWebSandbox as WebSandbox
3 changes: 3 additions & 0 deletions backend/modelscope_studio/components/pro/components.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from .chatbot import ModelScopeProChatbot as ProChatbot
from .monaco_editor import ModelScopeProMonacoEditor as ProMonacoEditor
from .monaco_editor.diff_editor import \
ModelScopeProMonacoEditorDiffEditor as ProMonacoEditorDiffEditor
from .multimodal_input import \
ModelScopeProMultimodalInput as ProMultimodalInput
from .web_sandbox import ModelScopeProWebSandbox as ProWebSandbox
106 changes: 106 additions & 0 deletions backend/modelscope_studio/components/pro/monaco_editor/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
from __future__ import annotations

from typing import Any, Literal, TypedDict, Union

from gradio.events import EventListener

from ....utils.dev import ModelScopeDataLayoutComponent, resolve_frontend_dir
from .diff_editor import ModelScopeProMonacoEditorDiffEditor


class LoaderConfig(TypedDict):
mode: Literal['cdn', 'local'] | None = None
cdn_url: str | None = None


class ModelScopeProMonacoEditor(ModelScopeDataLayoutComponent):
"""
"""
Comment on lines +17 to +18

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The docstring for the ModelScopeProMonacoEditor class is empty. Please add a comprehensive docstring explaining what the component does, its parameters, and how to use it. This is important for maintainability and for other developers to understand the component.

DiffEditor = ModelScopeProMonacoEditorDiffEditor

EVENTS = [
EventListener("mount",
callback=lambda block: block._internal.update(
bind_mount_event=True),
doc="An event is emitted when the editor is mounted."),
EventListener(
"change",
callback=lambda block: block._internal.update(bind_change_event=
True),
doc="An event is emitted when the editor value is changed."),
EventListener(
"validate",
callback=lambda block: block._internal.update(bind_validate_event=
True),
doc=
"An event is emitted when the editor value is changed and the validation markers are ready."
),
]

# supported slots
SLOTS = ["loading"]

# or { "mode": "cdn" }, default cdn: https://cdn.jsdelivr.net/npm/monaco-editor@xxx/min/vs
LOADER: Union[LoaderConfig, dict, None] = {"mode": "local"}

def __init__(
self,
value: str | None = None,
*,
# python, javascript etc.
language: str | None = None,
before_mount: str | None = None,
after_mount: str | None = None,
override_services: dict | None = None,
loading: str | None = "Editor is loading...",
options: dict | None = None,
line: int | None = None,
read_only: bool | None = None,
height: str | int | float | None = 400,
_loader: None = None,
_internal: None = None,
# gradio properties
visible: bool = True,
elem_id: str | None = None,
elem_classes: list[str] | str | None = None,
elem_style: dict | None = None,
render: bool = True,
**kwargs):
super().__init__(visible=visible,
value=value,
elem_id=elem_id,
elem_classes=elem_classes,
render=render,
elem_style=elem_style,
**kwargs)
self.line = line
self.loading = loading
self._loader = ModelScopeProMonacoEditor.LOADER
self.override_services = override_services
self.options = options
self.read_only = read_only
self.before_mount = before_mount
self.after_mount = after_mount
self.language = language
self.height = height

FRONTEND_DIR = resolve_frontend_dir("monaco-editor", type='pro')

@property
def skip_api(self):
return False

def api_info(self) -> dict[str, Any]:
return {"type": "string"}

def preprocess(self, payload: None | str) -> None | str:
return payload

def postprocess(self, value: None | str) -> None | str:
return value

def example_payload(self) -> Any:
return None

def example_value(self) -> Any:
return None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The example_value method currently returns None. To improve usability and testing, it's better to provide a meaningful example value, such as a sample code snippet string.

Suggested change
return None
return "print('Hello, World!')"

Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from __future__ import annotations

from typing import Any

from gradio.events import EventListener

from .....utils.dev import ModelScopeDataLayoutComponent, resolve_frontend_dir


class ModelScopeProMonacoEditorDiffEditor(ModelScopeDataLayoutComponent):
"""
"""
Comment on lines +11 to +12

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The docstring for the ModelScopeProMonacoEditorDiffEditor class is empty. Please add a comprehensive docstring explaining what the component does, its parameters, and how to use it.


EVENTS = [
EventListener("mount",
callback=lambda block: block._internal.update(
bind_mount_event=True),
doc="An event is emitted when the editor is mounted."),
EventListener(
"change",
callback=lambda block: block._internal.update(bind_change_event=
True),
doc="An event is emitted when the editor value is changed."),
EventListener(
"validate",
callback=lambda block: block._internal.update(bind_validate_event=
True),
doc=
"An event is emitted when the editor value is changed and the validation markers are ready."
),
]

# supported slots
SLOTS = ["loading"]

def __init__(
self,
value: str | None = None,
*,
original: str | None = None,
# python, javascript etc.
language: str | None = None,
original_language: str | None = None,
modified_language: str | None = None,
before_mount: str | None = None,
after_mount: str | None = None,
override_services: dict | None = None,
loading: str | None = "Editor is loading...",
read_only: bool | None = None,
options: dict | None = None,
line: int | None = None,
height: str | int | float | None = 400,
_loader: None = None,
_internal: None = None,
# gradio properties
visible: bool = True,
elem_id: str | None = None,
elem_classes: list[str] | str | None = None,
elem_style: dict | None = None,
render: bool = True,
**kwargs):
super().__init__(value=value,
visible=visible,
elem_id=elem_id,
elem_classes=elem_classes,
render=render,
elem_style=elem_style,
**kwargs)
from .. import ModelScopeProMonacoEditor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The local import from .. import ModelScopeProMonacoEditor inside the __init__ method is used to work around a circular dependency. This is a code smell and can lead to confusion and maintenance issues. A better approach would be to refactor the code to break the circular dependency. For example, you could move the shared LOADER configuration to a separate file that both modules can import from without creating a cycle.

Example refactor:

  1. Create a new file backend/modelscope_studio/components/pro/monaco_editor/config.py with the LOADER config.
  2. Import LOADER from config.py in both monaco_editor/__init__.py and monaco_editor/diff_editor/__init__.py.

self._loader = ModelScopeProMonacoEditor.LOADER
self.original = original
self.line = line
self.loading = loading
self.override_services = override_services
self.options = options
self.read_only = read_only
self.before_mount = before_mount
self.after_mount = after_mount
self.language = language
self.original_language = original_language
self.modified_language = modified_language
self.height = height

FRONTEND_DIR = resolve_frontend_dir("monaco-editor",
'diff-editor',
type='pro')

@property
def skip_api(self):
return False

def api_info(self) -> dict[str, Any]:
return {"type": "string"}

def preprocess(self, payload: None | str) -> None | str:
return payload

def postprocess(self, value: None | str) -> None | str:
return value

def example_payload(self) -> Any:
return None

def example_value(self) -> Any:
return None

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The example_value method currently returns None. To improve usability and testing, it's better to provide a meaningful example value, such as a sample code snippet string for the modified content.

Suggested change
return None
return "print('Hello, ModelScope Studio!')"

3 changes: 3 additions & 0 deletions docs/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,9 @@ def render(self):
"children": [{
"label": get_text("WebSandbox", "WebSandbox 网页沙盒"),
"key": "web_sandbox"
}, {
"label": get_text("MonacoEditor", "MonacoEditor 代码编辑器"),
"key": "monaco_editor"
}]
}]

Expand Down
88 changes: 88 additions & 0 deletions docs/components/pro/monaco_editor/README-zh_CN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# MonacoEditor

代码编辑器,[Monaco Editor](https://microsoft.github.io/monaco-editor/) 的 Gradio 实现,基于 [@monaco-editor/react](https://github.com/suren-atoyan/monaco-react) 集成。

## 示例

### 基本使用

<demo name="basic"></demo>

### Diff 编辑器

<demo name="diff_editor"></demo>

### Monaco Editor 配置项

可以直接传入 Monaco Editor 的配置项 [IStandaloneEditorConstructionOptions](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html)。

在这个示例中,我们关闭了编辑器的`minimap`与 `lineNumbers`。

<demo name="monaco_editor_options"></demo>

### 通过 JavaScript 定制化配置

如果你还需要更进一步地操作 Monaco Editor,你可以通过传入 JavaScript 函数字符串来进一步定制化配置。

<demo name="javascript_customize"></demo>

## API 

### 属性

#### MonacoEditor

| 属性 | 类型 | 默认值 | 描述 |
| ---------------- | --------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| value | `str\| None` | None | 编辑器的值。 |
| language | `str\| None` | None | 编辑器的语言(monaco-editor [支持](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages)的所有语言)。 |
| line | `number\| None` | None | 垂直滚动编辑器到指定行。 |
| read_only | `bool\| None` | None | 编辑器是否只读。 |
| loading | `str\| None` | 'Editor is loading...' | 编辑器初始化加载时的加载文案。 |
| options | `dict \| None` | None | [IStandaloneEditorConstructionOptions](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html) |
| overrideServices | `dict \| None` | None | [IEditorOverrideServices](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IEditorOverrideServices.html) |
| height | `str \| float \| int` | 400 | 组件的高度,如果值为数字,则以像素为单位指定,如果传递的是字符串,则以 CSS 单位指定。 |
| before_mount | `str \| None` | None | 传入 JavaScript 函数字符串,在编辑器加载前执行,可以获取到 `monaco` 对象。 |
| after_mount | `str \| None` | None | 传入 JavaScript 函数字符串,在编辑器加载后执行,可以获取到`editor`对象与`monaco` 对象。 |

#### MonacoEditor.DiffEditor

| 属性 | 类型 | 默认值 | 描述 |
| ----------------- | --------------------- | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| value | `str\| None` | None | 修改后的源的值(右侧)。 |
| original | `str\| None` | None | 原始源的值(左侧)。 |
| language | `str\| None` | None | 编辑器的语言(monaco-editor [支持](https://github.com/microsoft/monaco-editor/tree/main/src/basic-languages)的所有语言)。 |
| original_language | `str\| None` | None | 单独指定原始源的语言。否则,它将获取 language 属性的值。 |
| modified_language | `str\| None` | None | 单独指定修改后的源的语言。否则,它将获取 language 属性的值。 |
| line | `number\| None` | None | 垂直滚动编辑器到指定行。 |
| read_only | `bool\| None` | None | 编辑器是否只读。 |
| loading | `str\| None` | 'Editor is loading...' | 编辑器初始化加载时的加载文案。 |
| options | `dict \| None` | None | [IStandaloneEditorConstructionOptions](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IStandaloneEditorConstructionOptions.html) |
| overrideServices | `dict \| None` | None | [IEditorOverrideServices](https://microsoft.github.io/monaco-editor/typedoc/interfaces/editor.IEditorOverrideServices.html) |
| height | `str \| float \| int` | 400 | 组件的高度,如果值为数字,则以像素为单位指定,如果传递的是字符串,则以 CSS 单位指定。 |
| before_mount | `str \| None` | None | 传入 JavaScript 函数字符串,在编辑器加载前执行,可以获取到 `monaco` 对象。 |
| after_mount | `str \| None` | None | 传入 JavaScript 函数字符串,在编辑器加载后执行,可以获取到`editor`对象与`monaco` 对象。 |

### 事件

| 事件 | 描述 |
| ----------------------------------------------------------------- | ---------------------------------------- |
| `pro.(MonacoEditor \| MonacoEditor.DiffEditor).mount(fn, ···)` | 当编辑器加载完成时触发。 |
| `pro.(MonacoEditor \| MonacoEditor.DiffEditor).change(fn, ···)` | 当编辑器值改变时触发。 |
| `pro.(MonacoEditor \| MonacoEditor.DiffEditor).validate(fn, ···)` | 当编辑器触发校验并且校错误记存在时触发。 |

**注:** 根据 [monaco-editor](https://microsoft.github.io/monaco-editor/),只有具备丰富智能感知的语言才会触发 validate 事件。

- TypeScript
- JavaScript
- CSS
- LESS
- SCSS
- JSON
- HTML

### 插槽

```python
SLOTS=['loading']
```
Loading